Hoiatused, mis käivituvad üks kord, mitte igas serveris
Üks meie operatiivhoiatustest, „vähesed krediidid” avaldatud rakenduse jaoks, peaks jõudma meieni kõige rohkem üks kord päevas. Ühel pärastlõunal saabus see viis korda. Põhjus: meie API iga koopia meeldis mälus, millised hoiatused on juba saadetud, ja kaks serverit saatsid selle kaks korda ning iga deploy kustutas selle mälu ja lasi neil uuesti saata. Sama muster põhjustas ka selle, et taustatöö tehti kaks korda. Mõlemad koordineerivad nüüd jagatud salvestuse kaudu ja mõlemad ebaõnnestuvad turvaliselt, kui see salvestus pole kättesaadav.
Sümptom
Mythex saadab oma meeskonnale hoiatusi, kui midagi vajab vaatamist: teenus on maas, töö on kinni jäänud või ehitaja avaldatud rakenduse krediidisaldo on otsakorral. Igal hoiatusel on dubleerimise aken, ehk ajavahemik, mille jooksul sama hoiatus saadetakse ainult üks kord. Vähese krediidi hoiatuse puhul on see aken 24 tundi.
Viis sama hoiatuse koopiat ühe pärastlõuna jooksul tähendas, et aken ei hoidnud. Hullem oli see, et need tulid paaridena, umbes 11 minuti vahega.
Miks see juhtus
Meie hoiatuste pakett hoidis dubleerimist mälus: tabel „hoiatuse võti → millal viimati saadeti” töötava protsessi sees. See toimib siis, kui on üks protsess, mis ei taaskäivitu kunagi. Tootmises pole kumbagi:
- Kaks API replica'd. Tootmises töötab kaks API koopiat. Igaühel oli oma tabel, nii et igaüks saatis hoiatuse ühe korra. Need on paarid.
- Deploy'd. Iga deploy asendab mõlemad replica'd uute protsessidega ja tühjade tabelitega. Pärast deploy'd saadeti hoiatus uuesti, justkui poleks seda kunagi saadetud.
Kahe paari hoiatuse vaheline ~11 minuti vahe tuli tööst, mis tekitab selle hoiatuse. See töö jookseb mõlemal replical ja võtab lukku, nii et replica'd võtavad selle järjest, ja igaüks tõstatas hoiatuse, kui oli tema kord.
Samas koodis oli ka teine, vaiksem viga. Et tabel ei kasvaks lõputult, kustutas see end, kui sinna jõudis 500 võtit. See kustutas kõik kirjed, mis olid vanemad kui 15 minutit, olenemata nende aknast. Koormuse all võis 24-tunnine dubleerimine kesta veerand tundi.
Parandus
Üks jagatud kirje iga hoiatuse kohta
Enne kui API hoiatuse saadab, võtab ta nüüd hoiatuse dubleerimisvõtme Redis'est, jagatud mälusalvestusest, millega mõlemad replica'd suhtlevad ja mis ei taaskäivitu siis, kui API taaskäivitub. Võtme võtmine on üks operatsioon: „sea see võti ainult siis, kui teda pole, ja aegu pärast akent”. Esimene replica, kes võtme saab, saadab hoiatuse. Teine leiab võtme juba võetuna ja jääb vaikseks. Pärast deploy'd leiab uus protsess võtme ikka veel alles.
Võti ja aken tulevad samast reeglist, mida hoiatuste pakett kasutab, ja see on nüüd mõlema kihi vahel jagatud. Nii ei saa need kaks kihti lahku minna selles, mis loetakse „sama hoiatuseks”.
Ebaõnnestu avatult, mitte suletult
Kõige olulisem otsus oli see, mida teha, kui Redis'ega ei saa ühendust. Me otsustasime hoiatuse ikkagi saata. Kui Redis on maas või ei vasta 1,5 sekundi jooksul, läheb hoiatus välja. Dubleeritud hoiatus on müra. Kadunud hoiatus tähendab, et keegi ei saa teada.
Kõige tõsisemaid hoiatusi, P0, dubleerimine üldse ei peata.
Igal kirjel on oma aken
Hoiatuste paketi enda mälusisesel dubleerimisel salvestab iga kirje nüüd, millal tema aken lõpeb, ja puhastamine eemaldab ainult need kirjed, mille aken on lõppenud. 24-tunnine võti jääb alles 24 tunniks, olenemata sellest, kui palju teisi hoiatusi saabub.
Sama viga, mujal
Sama muster oli kahekordistanud ka ühe taustatöö: sünkroonimistöö, mis hoiab meie kirjet iga töötoa oleku kohta joondatuna meie liivakasti pakkuja omaga. Iga läbijooks loetleb kõik liivakastid kontos (tootmises tuhandeid, umbes viis sekundit) ja kõik töötavad või peatatud projektid. Mõlemad replica'd jooksutasid seda iga kahe minuti järel.
Selle kirjutamised on idempotentsed, see tähendab, et kahe korra jooksutamine annab sama tulemuse kui ühe korra, nii et midagi ei läinud katki. Lihtsalt tööd oli kaks korda rohkem. Nüüd võetakse iga kahe minuti aken Redis'es samamoodi, nagu meie kasutusmõõdikud oma aknad juba võtsid, ja jooksutab läbijooksu ainult see replica, kes akna võtab. Kui Redis'ega ei saa ühendust, jookseb läbijooks ikkagi: töö tegemine kaks korda on kahjutu, tegemata jätmine ei ole.
Kuidas me testisime
Testid mudeldavad päris seadistust. Need laadivad hoiatuste mooduli kaks korda, nagu oleks tegu kahe eraldi „replica'ga”, suunavad mõlemad ühele jagatud salvestusele, tõstatavad igast neist sama hoiatuse ja laadivad seejärel kolmanda värske koopia, et simuleerida „pärast deploy'd”, ning tõstatavad hoiatuse uuesti. Täpselt üks hoiatus saadetakse. Teised testid kontrollivad, et teise ehitaja hoiatus läheb ikka välja, et P0 saadetakse mõlemast replicast, ja et hoiatus saadetakse ka siis, kui salvestus on maas või ületab ajalimiidi. Puhastamise parandusel on oma test: 24-tunnine võti jääb alles, kui 600 teist hoiatust saabuvad 20 minuti pärast.
Taustatöö jaoks: kaks replica'd, mis käivituvad samal ajal, plus kolmas käivitus samas aknas, annavad ühe nimekirja.
Mida sellest õppisime
- Mälusisene olek on protsessipõhine. Kõik, mis peab servrite vahel tõene olema, „saadetud üks kord” või „jooksis üks kord”, peab elama kohas, mida nad jagavad.
- Deploy'd on taaskäivitused. Olek, mis püsib ainult kuni järgmise taaskäivituseni, ei püsi tavalise tööpäeva jooksul.
- Otsusta, mis suunas ebaõnnestuda. Hoiatuste puhul peaks jagatud salvestuse maas olemine tähendama rohkem hoiatusi, mitte vähem.
- Otsi mustrit, mitte ainult juhtumit. Hoiatus oli valjuhäälne juhtum; kahekordistatud sünkroonimistöö oli sama viga, mis vaikselt raisas tööd.
Sellest pole Mythexi seest midagi näha. Platvormi stabiilsuse hoidmisest loe lisaks kuidas me saime deploy'd nullini 502 vigadeni ja puuduv swap-fail. Kui mõte mitme serveri koopia jooksutamisest on sulle uus, aitab meie juhend mis on server põhitõdedest.