Ang Pagpindot ng Publish nang Dalawang Beses ay Mag-p-publish Na Isang Beses Lang
Tatlong publish request para sa iisang app ang umabot sa API namin nang 84 milliseconds ang pagitan, at bawat isa ay nagsimula ng buong deploy ng app na iyon. Nagbanggaan sila sa iisang hosting, at ilan sa mga nagresultang release ay pumalya dahil may nauna nang nakarating. Ang tanging nakikitang senyales ay isang email na “release failed”, at maayos naman sa huli ang mismong app. Ngayon, ang publish na dumating habang may tumatakbo nang isa para sa parehong app ay hindi na nagsisimula ng pangalawang build. Ang database ang nagpapasya kung aling request ang mananalo, at tapat na sinasabi sa iba ang totoo: may publish nang tumatakbo.
Ano ang nangyari
Kapag nag-publish ka ng app sa Mythex, nagtatala ang API ng deployment para dito at hinihiling sa aming orchestrator, ang serbisyong nagpapatakbo ng mga build, na buuin at ilabas ito. Puwedeng magsimula ang publish sa Publish button, sa ibang browser tab, o sa agent kapag hiniling mong mag-publish ito. Ang dobleng pag-click, dalawang tab, o agent request na dumating nang dalawang beses ay puwedeng magbunga ng higit sa isang publish para sa parehong app sa halos iisang sandali.
Sa kasong ito, tatlo ang dumating sa loob ng 84 ms. Bawat isa ay nagpatakbo ng buong deploy. Sa hosting platform, nalikha ang mga release para sa parehong app sa loob ng iisang segundo, at tatlo sa mga ito ay pumalya dahil nasa progreso na ang iba. Ang isang release na dumating sa huli ay natigil, at gumana ang app pagkatapos.
Iyon ang nagpadali sa hindi mapansin. Isang gumaganang app ang nakita ng user. Lumabas lang ang mga pagkabigo bilang mga email na release-failure.
Bakit walang pumigil dito
May hakbang nga ang publish code na tumitingin sa mga deploy na nasa progreso para sa project. Pero ang ginagawa nito ay markahan bilang failed ang bawat deploy na nasa progreso bago magsimula. “Pinalitan ng bagong publish” ang dahilan. Layunin nitong linisin ang mga build na naiwan ng nag-crash na process.
Sa praktika, ang ibig sabihin nito ay ang pangalawang publish na dumating habang nagbi-build ay idedeklarang patay na ang una at magsisimula ng sarili nitong build. Parehong tumakbo hanggang matapos laban sa iisang app. Walang lock; may hakbang na aktibong nagbubukas ng daan para sa race.
May pangalawa, mas kakaibang sintomas para sa unang publish ng isang app. Dahil wala pang deployment record na magagamit muli, dalawang sabay na unang publish ang parehong sumubok magpasok ng iisa. May unique index ang database sa link name ng app (ang slug nito, gaya ng your-app sa your-app.mythex.ai), kaya pumalya ang pangalawang insert. Isinalin ito ng API bilang “taken na ang link name na iyon”, at pinayuhan ang user na palitan ang pangalan ng app na, sa mismong sandaling iyon, ay matagumpay na nagpa-publish sa ilalim ng pangalang iyon.
Ang ayos: ang pag-angkin ang lock
Ang pag-angkin sa deployment record ay isa nang kondisyonal na update sa database: kunin ang row na ito at markahan itong building kung walang nagpa-publish nito. Dahil iisang statement ito, inaayos ng database ang mga request na dumarating sa parehong millisecond. Eksaktong isa lang ang nagbabago sa row. Nakikita ng iba na zero rows ang nabago, humihinto, at hindi kailanman nakakarating sa orchestrator.
Kailangan din ng lock ng paraan palabas kapag namatay ang may hawak nito. Kung nag-restart ang API sa gitna ng publish, mananatiling markahang “building” ang row magpakailanman at hindi na ma-publish ang app. Kaya ang row na tumigil nang magpadala ng heartbeat ang may-ari (ina-update ng tumatakbong publish ang row bawat 30 segundo, at ang limang minutong katahimikan ay binibilang bilang patay) ay puwede nang angkinin muli. Galing sa heartbeat ang threshold na iyon, hindi sa hula kung gaano katagal ang isang build. Puwedeng tumagal nang husto ang pagbuo ng sariling Dockerfile ng user, at ang mabagal na build na nagha-heartbeat pa rin ay nananatili sa claim nito.
Hindi na tumatakbo bago ang publish ang hakbang na nagpapabagsak sa bawat deploy na nasa progreso. Ang mga row lang na naiwan ng patay na process ang nililinis.
Pagsabi sa natalo ng totoo
Ang request na natalo ay may malinaw nang sagot: publish_in_progress, kasama ang ID ng deployment na tumatakbo. Hinahawakan ito ng bawat lugar na maaaring magsimula ng publish:
- Sinusundan ng workspace ang build na tumatakbo na sa halip na magpakita ng error sa ibabaw nito.
- Sinasabihan ang agent na may tumatakbong publish ng app na ito at maghintay dito, sa halip na sabihing pumalya ang isang bagay, na dati ay maaaring magtulak dito na “ayusin” ang Dockerfile at subukan ulit.
- Sinusuri ang banggaan sa unang publish sa link name: kung ang row na may hawak ng pangalan ay ang sariling tumatakbong publish ng project na ito, ang sagot ay “nagpa-publish na”, hindi “taken na ang pangalan”. Makakakuha pa rin ng mensahe ng rename ang tunay na banggaan sa ibang project.
Paano namin ito sinubok
Sumulat kami ng reproduction script na nagpapatakbo ng tatlong publish ng iisang app nang sabay laban sa tunay na database at stub orchestrator, at binibilang kung ilang deploy ang hinihingi sa orchestrator. Sakop nito ang unang publish at ang republish ng live na app. Bago ang ayos, dalawang deploy ang nabilang. Pagkatapos, isa.
Sakop din ng script ang escape hatch: minamarkahan nito ang row ng app bilang building na walang heartbeat sa loob ng sampung minuto, nagpa-publish ulit, at tinitingnan kung inaangkin ng bagong publish ang row at tumatakbo ito.
Tinatakda ng mga unit test ang lock sa dalawang bagay na dapat gawin ng lock: sa tatlong sabay na republish, eksaktong isa ang nakakalusot at sinasabihan ang dalawa na may tumatakbong publish; at puwedeng angkinin ang build na namatay ang may-ari. May isa pang test na nagsusuri na hindi dinadala ng bagong pagtatangka ang error message ng nakaraang pagtatangka.
Ang natutunan namin
- Puwedeng maging race sa disguise ang cleanup code. Ang “pumalya ang anumang nasa progreso, saka magsimula” ay mukhang kalinisan. Sa dalawang tumatawag, bawat isa ay kinakansela ang isa.
- Hayaan ang database na magpasya. Ang pagbasa na sinundan ng pagsulat ay nag-iiwan ng puwang para sa ibang request. Ang iisang kondisyonal na update ay hindi.
- Bawat lock ay kailangan ng expiry na nakatali sa buhay ng proseso. Kung wala nito, nagiging app na nakabara ang crash.
- Ang mga error message ay mga tagubilin. Ang “taken na ang pangalang iyon” ay nagturo sa mga tao na gumawa ng maling bagay. Mas simple at totoo ang tamang mensahe.
Mas marami pang detalye tungkol sa publishing sa docs: paano mag-publish. At isang kaugnay na ayos: nananatiling online ang published app mo habang nire-republish ito.