Blogg

Att trycka på Publish två gånger publicerar nu bara en gång

Tre publiceringsförfrågningar för samma app nådde vårt API med 84 millisekunders mellanrum, och varje startade en full deploy av appen. De tävlade om samma hosting, flera av de resulterande releaserna misslyckades eftersom en annan hade kommit fram först, och det enda synliga tecknet var ett e-postmeddelande om ”release failed” — appen i sig blev till slut bra. Nu startar en publicering som kommer in medan en annan redan körs för samma app inte en andra build. Databasen avgör vilken förfrågan som vinner, och de andra får veta sanningen: en publicering pågår redan.

Vad som hände

När du publicerar en app på Mythex skapar API:et en deployment för den och ber vår orkestrerare — tjänsten som kör builds — att bygga och släppa ut den. Publicering kan starta från knappen Publish, från en annan webbläsarflik eller från agenten när du ber den publicera. Ett dubbelklick, två flikar eller en agentförfrågan som kommer två gånger kan alla ge fler än en publicering för samma app nästan samtidigt.

I det här fallet kom tre inom 84 ms. Varje körde en full deploy. På hostingplattformen skapades releaser för samma app under samma sekund, och tre av dem misslyckades eftersom de andra redan pågick. En senare release fastnade, och appen fungerade efteråt.

Det var det som gjorde det lätt att missa. Användaren såg en fungerande app. Felen syntes bara som e-postmeddelanden om misslyckad release.

Varför inget stoppade det

Publiceringskoden hade visserligen ett steg som tittade på pågående deployer för projektet. Men det den gjorde var att markera varje pågående deploy som misslyckad innan den startade — ”superseded by a new publish”. Syftet var att städa upp builds som lämnats kvar av en krashad process.

I praktiken innebar det att en andra publicering som kom mitt i en build förklarade den första död och startade sin egen. Båda kördes till slut mot samma app. Det fanns inget lås; det fanns ett steg som aktivt röjde väg för en kapplöpning.

Det fanns också ett andra, märkligare symtom vid en apps allra första publicering. Utan en deployment-post att återanvända försökte två samtidiga första publiceringar båda skapa en. Databasen har ett unikt index på appens länknamn (dess slug, till exempel your-app i your-app.mythex.ai), så den andra infogningen misslyckades. API:et översatte det till ”det länknamnet är upptaget” — vilket uppmanade användaren att byta namn på en app som just då publicerades framgångsrikt under det namnet.

Lösningen: anspråket är låset

Att göra anspråk på deployment-posten är nu en enda villkorlig databasuppdatering: ta den här raden och markera den som building bara om ingen publicerar den. Eftersom det är ett enda uttryck avgör databasen förfrågningar som kommer inom samma millisekund. Exakt en av dem ändrar raden. De andra ser att noll rader ändrades, stannar och når aldrig orkestreraren.

Ett lås behöver också en utväg när dess innehavare dör. Om API:et startas om mitt i en publicering skulle raden stå kvar som ”building” för evigt och appen gick inte att publicera. Så en rad vars ägare har slutat skicka heartbeats — den pågående publiceringen uppdaterar raden var 30:e sekund, och fem minuters tystnad räknas som död — kan göra anspråk på igen. Den tröskeln kommer från heartbeaten, inte från en gissning om hur lång en build tar. Att bygga användarens egen Dockerfile kan legitimt ta lång tid, och en långsam build som fortfarande skickar heartbeats behåller sitt anspråk.

Steget som markerade alla pågående deployer som misslyckade körs inte längre före en publicering. Endast rader som en död process lämnat kvar rensas.

Att berätta sanningen för den som förlorar

Förfrågan som förlorar får nu ett tydligt svar: publish_in_progress, tillsammans med ID:t för deployment som kör. Varje ställe som kan starta en publicering hanterar det:

  • Arbetsytan följer den build som redan körs i stället för att visa ett fel ovanpå den.
  • Agenten får veta att en publicering av appen redan pågår och att den ska vänta på den, i stället för att få veta att något misslyckats — vilket tidigare kunde skicka iväg den för att ”fixa” en Dockerfile och försöka igen.
  • En krock vid första publicering på länknamnet kontrolleras: om raden som håller namnet är projektets egen pågående publicering blir svaret ”already publishing”, inte ”that name is taken”. En verklig krock med ett annat projekt får fortfarande meddelandet om att byta namn.

Så kontrollerade vi det

Vi skrev ett reproduktionsskript som kör tre publiceringar av samma app samtidigt mot en riktig databas och en stubb-orkestrerare, och räknar hur många deployer orkestreraren begärs att göra. Det täcker både en första publicering och en ompublicering av en live-app. Före fixen räknade den två deployer. Efter fixen en.

Skriptet täcker också nödutgången: det markerar appens rad som building utan heartbeat i tio minuter, publicerar på nytt och kontrollerar att den nya publiceringen gör anspråk på raden och körs.

Enhetstester håller låset till de två saker ett lås måste göra: av tre samtidiga ompubliceringar släpps exakt en igenom, och de andra två får veta att en publicering pågår; och en build vars ägare dog kan göra anspråk på. Ett annat test kontrollerar att ett nytt försök inte bär med sig föregående försöks felmeddelande.

Vad vi tog med oss

  • Städkod kan vara en kapplöpning i förklädnad. ”Misslyckas allt som pågår, och starta sedan” låter som hygien. Med två anropare blir det att var och en avbryter den andra.
  • Låt databasen avgöra. En läsning följd av en skrivning lämnar ett gap för en annan förfrågan. En enda villkorlig uppdatering gör det inte.
  • Varje lås behöver en utgång kopplad till liveness. Utan den blir en krasch till en fastnad app.
  • Felmeddelanden är instruktioner. ”That name is taken” fick folk att göra fel sak. Det rätta meddelandet var enklare och sant.

Mer om publicering finns i dokumentationen: how to publish. Och en relaterad fix: din publicerade app förblir uppe när du publicerar om den.

Mythex · Pricing · Bygg vad som helst med AI · Mallar för webbplatser och appar · Mythex jämfört · Ordlista för apputveckling med AI · Gratis verktyg för apputveckling med 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 Srpski Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)