Blog

Opozorila, ki se sprožijo enkrat, ne enkrat na strežnik

Eno od naših operativnih opozoril — „nizko stanje kreditov“ za objavljeno aplikacijo graditelja — naj bi nas doseglo največ enkrat na dan. Nekega popoldneva je prišlo petkrat. Vzrok: vsaka kopija našega API-ja si je opozorila, ki jih je že poslala, zapomnila v svojem pomnilniku, zato sta dva strežnika poslala opozorilo dvakrat, vsaka namestitev pa je ta pomnilnik izbrisala in jima dovolila, da ga pošljeta znova. Podoben vzorec je imel tudi ozadni opravilo, ki je delo opravljal dvakrat. Oboje se zdaj usklajuje prek deljene shrambe in oboje ob nedostopnosti te shrambe pade na varno stran.

Simptom

Mythex pošilja opozorila ekipi, kadar nekaj potrebuje pozornost: storitev ne deluje, opravilo se je ustavilo, stanje kreditov graditelja pa pada, medtem ko je njegova aplikacija objavljena. Vsako opozorilo ima okno deduplikacije — obdobje, v katerem se isto opozorilo pošlje samo enkrat. Pri opozorilu o nizkem stanju kreditov je to okno 24 ur.

Pet kopij istega opozorila v enem popoldnevu je pomenilo, da okno ni zdržalo. Še huje, prihajala so v parih, približno 11 minut narazen.

Zakaj se je to zgodilo

Paket z opozorili je deduplikacijo hranil v pomnilniku: tabelo „ključ opozorila → kdaj je bilo zadnjič poslano“, znotraj tečočega procesa. To deluje, kadar obstaja en sam proces, ki se nikoli ne ponovno zažene. Produkcija nima ne enega ne drugega:

  • Dve repliki API-ja. Produkcija teče z dvema kopijama API-ja. Vsaka je imela svojo tabelo, zato je vsaka opozorilo poslala enkrat. To so pari.
  • Namestitve. Vsaka namestitev zamenja obe repliki s svežima procesoma in praznima tabelama. Po namestitvi je opozorilo šlo ven znova, kot da nikoli ni bilo poslano.

Na ~11-minutni razmik med dvema opozoriloma v vsakem paru vpliva opravilo, ki sproža to opozorilo. Teče na obeh replikah in si vzame zaklep, zato replike zmagujejo izmenično, vsaka pa je opozorilo sprožila, ko je bil na vrsti njen krog.

V isti kodi je bila še ena, tišja napaka. Da tabela ne bi rasla v nedogled, se je samodejno počistila, ko je imela 500 ključev, in izbrisala vsak vpis, starejši od 15 minut, ne glede na njegovo okno. Pod obremenitvijo je 24-urna deduplikacija lahko trajala samo četrt ure.

Popravek

En deljeni zapis za vsako opozorilo

Preden API pošlje opozorilo, zdaj v Redisu zahteva ključ deduplikacije za to opozorilo — deljeno shrambo v pomnilniku, s katero se pogovarjata obe repliki in ki se ne ponastavi, ko se ponastavi API. Zahteva je ena operacija: „nastavi ta ključ samo, če ne obstaja, in ga poteči po preteku okna“. Replika, ki ga prva zahteva, pošlje opozorilo; druga najde ključ zasedel in ostane tiho. Nov proces po namestitvi najde ključ še vedno tam.

Ključ in okno prihajata iz istega pravila, ki ga zdaj uporabljata oba sloja paketa za opozorila, zato se ne moreta razhajati glede tega, kaj šteje za „isto opozorilo“.

Odpovej odprto, ne zaprto

Najpomembnejša odločitev je bila, kaj storiti, ko Redisa ni mogoče doseči. Odločili smo se, da opozorilo vseeno pošljemo. Če je Redis nedosegljiv ali ne odgovori v 1,5 sekunde, opozorilo gre ven. Podvojeno opozorilo je šum. Izgubljeno opozorilo pomeni, da nekdo ni bil obveščen.

Najbolj resna opozorila, P0, deduplikacija sploh nikoli ne zadrži.

Vsak vpis ohrani svoje okno

V lastni deduplikaciji paketa za opozorila v pomnilniku zdaj vsak vpis hrani, kdaj se konča njegovo okno, čiščenje pa odstrani samo vpise, katerih okno je poteklo. 24-urni ključ ostane 24 ur, ne glede na to, koliko drugih opozoril pride vmes.

Ista napaka, drugje

Isti vzorec je podvojil tudi kos ozadnega dela: uskladitveno opravilo, ki naše evidence o stanju delovnega prostora vsakega uporabnika usklajuje s stanjem pri ponudniku peskovnikov. Vsak prehod seznam vseh peskovnikov v računu — v produkciji jih je na tisoče, traja okoli pet sekund — in vsak tekoči ali zaustavljen projekt. Obe repliki sta ga zagnali vsaki dve minuti.

Njegovi zapisi so idempotentni — če se izvedejo dvakrat, je rezultat enak kot pri enkratnem izvajanju — zato nič ni bilo pokvarjeno. Bilo je preprosto dvakrat toliko dela. Zdaj se vsako dvominutno okno v Redisu zahteva na enak način, kot so si naši števci porabe že zahtevali svoja okna, in opravilo zažene samo replika, ki okno zahteva. Če Redisa ni mogoče doseči, se prehod vseeno izvede: delo, opravljeno dvakrat, ni škodljivo, neopravljeno delo pa je.

Kako smo testirali

Testi posnemajo pravo postavitev. Modul za opozorila naložijo dvakrat, kot dve ločeni „repliki“, obe usmerijo v isto deljeno shrambo, z vsake pošljejo isto opozorilo, nato naložijo tretjo, svežo kopijo, ki nadomesti „po namestitvi“, in opozorilo sprožijo znova. Pošlje se natanko eno opozorilo. Drugi testi preverijo, da opozorilo drugega graditelja še vedno gre ven, da P0 gre ven iz obeh replik in da se opozorilo še vedno pošlje, kadar je shramba nedosegljiva ali se zatakne čez časovno omejitev. Popravek čiščenja ima svoj test: 24-urni ključ preživi 600 drugih opozoril, ki prispejo 20 minut kasneje.

Za uskladitveno opravilo: dve repliki, ki tiktakata hkrati, plus tretji tik v istem oknu, dajo en sam seznam.

Kaj smo iz tega vzeli

  • Stanje v pomnilniku pripada procesu. Vse, kar mora veljati čez strežnike — „poslano enkrat“, „izvedeno enkrat“ — mora živeti tam, kjer si ga delijo.
  • Namestitve so ponovni zagoni. Stanje, ki preživi samo do naslednjega ponovnega zagona, ne preživi običajnega delovnega dne.
  • Odloči se, v katero smer odpovedati. Pri opozorilih naj nedostopnost deljene shrambe pomeni več opozoril, ne manj.
  • Išči vzorec, ne samo primerka. Opozorilo je bil glasni primer; podvojeno uskladitveno opravilo je bila ista napaka, ki tiho stane delo.

Nič od tega ni vidno znotraj Mythexa. Za več o tem, kako ohranjamo platformo stabilno, glej kako smo namestitve spravili na nič napak 502 in manjkajočo datoteko swap. Če ideja, da teče več kopij ene storitve, ni domača, naš vodič o kaj je strežnik pokrije osnove.

Mythex · Pricing · Zgradite karkoli z AI · Predloge za spletna mesta in aplikacije · Mythex v primerjavi · Slovar pojmov gradnje aplikacij z AI · Brezplačna orodja za gradnjo aplikacij 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 ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)