Riasztások, amelyek egyszer szólnak, nem szerverenként egyszer
Az egyik működési riasztásunk — „alacsony kredit” egy builder publikált appjához — egyszer érkezne meg naponta. Egy délután viszont ötször jött meg. Az ok: az API minden példánya a saját memóriájában jegyezte, mely riasztásokat küldte már el, így a két szerver kétszer küldte el, és minden deploy törölte ezt a memóriát, így újra elküldték. Ugyanez a minta egy háttérfeladatot is kétszer futtatott le. Mindkettő most egy közös tárolón keresztül koordinálja magát, és ha a tároló nem érhető el, mindkettő a biztonságos oldalra dől.
A tünet
A Mythex csapatriasztásokat küld, ha valaminek figyelemre van szüksége: egy szolgáltatás leállt, egy feladat elakadt, vagy egy builder kreditegyenlege fogy, miközben az appja publikálva van. Minden riasztásnak van deduplikációs ablaka — egy időszak, amely alatt ugyanaz a riasztás csak egyszer megy ki. Az alacsony kredit riasztásnál ez az ablak 24 óra.
Öt egyforma riasztás egy délutánon azt jelentette, hogy az ablak nem tartott. Ami rosszabb: párban jöttek, nagyjából 11 perces különbséggel.
Miért történt
Az alerts csomagunk a deduplikációt memóriában tartotta: egy táblázat, amely „riasztáskulcs → mikor küldtük utoljára” párokat tárol, a futó folyamaton belül. Ez akkor működik, ha egy folyamat van, amely sosem indul újra. A productionben egyik sem igaz:
- Két API-replika. A productionben az API két példányban fut. Mindegyiknek külön táblázata volt, így mindegyik egyszer küldte el a riasztást. Ezek a párok.
- Deployok. Minden deploy mindkét replikát új folyamatokkal és üres táblázatokkal cseréli le. Deploy után a riasztás újra kiment, mintha soha nem küldték volna el.
A két riasztás közötti ~11 perces különbség abból a feladatból származott, amely ezt a riasztást előállítja. Ez mindkét replikán fut, és zárat vesz, így a replikák felváltva nyerik el, és mindegyik akkor emelte a riasztást, amikor sora került.
A ugyanebben a kódban volt egy második, csendesebb hiba is. Hogy a táblázat ne nőjön a végtelenségig, 500 kulcsnál elkezdte önmagát metszeni: minden 15 percnél régebbi bejegyzést törölt, függetlenül az ablakától. Terhelés alatt egy 24 órás deduplikáció negyed óráig tartott.
A javítás
Egy közös rekord riasztásonként
Mielőtt az API riasztást küld, most a riasztás deduplikációs kulcsát Redisben foglalja le — ez egy közös memóriatároló, amelyhez mindkét replika csatlakozik, és amely nem indul újra, ha az API újraindul. A foglalás egyetlen művelet: „ezt a kulcsot csak akkor állítsd be, ha még nem létezik, és az ablak végén járjon le”. Amelyik replika előbb foglal, az elküldi a riasztást; a másik azt találja, hogy a kulcs foglalt, és csendben marad. Egy deploy utáni új folyamat is megtalálja még a kulcsot.
A kulcs és az ablak ugyanabból a szabályból származik, amelyet az alerts csomag használ, de most a kettő közösen használja, így a két réteg nem térhet el abban, mi számít „ugyanannak a riasztásnak”.
Hibánál nyitva maradjon, ne zárva
A legfontosabb döntés az volt, mi történjen, ha a Redis nem érhető el. Úgy döntöttünk, hogy a riasztás mindenképp kimegy. Ha a Redis nem működik, vagy 1,5 másodpercen belül nem válaszol, a riasztás elmegy. Egy duplikált riasztás zaj. Egy elveszett riasztás azt jelenti, hogy valakit nem értesítettek.
A legsúlyosabb riasztásokat, a P0-kat a deduplikáció egyáltalán nem tartja vissza.
Minden bejegyzés megtartja a saját ablakát
Az alerts csomag saját memóriabeli deduplikációjában most minden bejegyzés tárolja, mikor ér véget az ablaka, a metszés pedig csak azokat a bejegyzéseket távolítja el, amelyek ablaka lejárt. Egy 24 órás kulcs 24 óráig megmarad, függetlenül attól, hány másik riasztás érkezik.
Ugyanez a hiba máshol
Ugyanez a minta egy háttérfeladatot is megduplázott: egy egyeztető feladatot, amely a munkaterületek állapotának nyilvántartását a sandbox-szolgáltatónk adataihoz igazítja. Minden körben listázza a fiók összes sandboxát — a productionben több ezret, körülbelül öt másodperc alatt —, plusz minden futó vagy szünetelő projektet. Mindkét replika két percenként futtatta.
Az írásai idempotensek — egyszeri és kétszeri futtatásuk ugyanazt az eredményt adja —, így semmi nem tört el. Csak kétszer annyi munka volt. Most minden két perces ablakot Redisben foglalunk le ugyanúgy, ahogy a használati mérőink is foglalták a sajátjaikat, és csak az a replika futtatja a kört, amelyik a foglalást megszerezte. Ha a Redis nem érhető el, a kör mégis lefut: a munka kétszeri elvégzése ártalmatlan, a munka elmaradása nem az.
Hogyan teszteltük
A tesztek a valódi felállást modellezik. Az alerts modult kétszer töltik be, mint két külön „replikát”, mindkettőt egy közös tárolóra irányítják, mindkettőből kiemelnek ugyanazt a riasztást, majd betöltenek egy harmadik, friss példányt, hogy egy „deploy utáni” állapotot szimuláljanak, és újra kiemelik. Pontosan egy riasztás megy ki. További tesztek ellenőrzik, hogy egy másik builder riasztása is kimegy, hogy egy P0 mindkét replikából elmegy, és hogy a riasztás akkor is kimegy, ha a tároló leállt vagy az időkeretet túllépi. A metszés javításának saját tesztje van: egy 24 órás kulcs túléli, miközben 600 másik riasztás érkezik 20 perccel később.
Az egyeztető feladathoz két replika ugyanabban a pillanatban ketyeg, plusz egy harmadik ketyegés ugyanabban az ablakban, és ezek egyetlen listázást eredményeznek.
Amit ebből tanultunk
- A memóriabeli állapot folyamatonként létezik. Minden, aminek szervereken át igaznak kell lennie — „egyszer küldve”, „egyszer futtatva” —, olyan helyen kell éljen, amelyet megosztanak.
- A deployok újraindítások. Az az állapot, amely csak a következő újraindításig marad meg, egy átlagos munkanapon nem marad meg.
- Döntsd el, melyik irányba hibázzon a rendszer. Riasztásoknál egy leálló közös tároló legyen több riasztás, ne kevesebb.
- A mintát keresd, ne csak az előfordulást. A riasztás volt a hangos eset; a megduplázott egyeztető feladat ugyanez a hiba volt, csendben pazarolva a munkát.
Az egész nem látszik a Mythexen belülről. Arról, hogyan tartjuk stabilan a platformot, lásd hogyan értük el, hogy a deployok során nulla 502-es hiba legyen és a hiányzó swap-fájl cikkeinket. Ha új vagy abban, hogy egy szolgáltatásból több példány fut, az mi a szerver? útmutatónk az alapokat tárgyalja.