Hvernig eitt þungt spjall gat slegið API-ið okkar út
Þann 28. september, um 17 mínútum eftir útgáfu, runnu báðir API-netþjónarnir okkar í framleiðslu út úr minni á sama augnabliki, endurræstust, og í um fimm mínútur hafði álagsjafnarinn engan heilbrigðan netþjón til að senda umferð á. Fyrsta lagfæringin gaf API-inu tvöfalt minni. Raunveruleg orsök kom í ljós síðar sama dag: það að opna eitt tiltekið spjall fékk API-ið til að hlaða 53 MB af skilaboðum í einni beiðni. Spjallsaga hleðst nú í síður sem takmarkaðar eru bæði eftir stærð og fjölda, og það spjall opnast með 5 MB fyrstu síðu í stað hruns.
Hvað fór niður
API-ið okkar keyrir sem gámur á hvorum tveggja netþjóna á bak við álagsjafnara. Hver gámur hafði 384 MiB minnismörk. Í hvíld notar hann 130–140 MiB, svo mörkin virtust hafa nóg rými.
Báðir gámarnir náðu mörkunum saman og hrundu með „JavaScript heap out of memory“. Haugurinn er minnið sem Node.js ferli notar fyrir hlutina sem það vinnur með. Þar sem báðir netþjónar hrundu í einu var hvergi eftir að beina beiðnum. Það er versta tegund af útfalli fyrir uppsetningu með tveimur netþjónum: varaleiðin hjálpar aðeins þegar bilanir gerast ekki samtímis.
Fyrsta lagfæringin: meira rými
Tímasetningin benti á útgáfuna. Eftir útgáfu eru lotur agentsins sem útgáfan truflaði endurræstar og vista afrit sín aftur, og sú hrina af vinnu leit út eins og augljós uppspretta minnisfjaðrar.
Svo fyrsta breytingin hækkaði mörk API-sins úr 384 MiB í 768 MiB. Við beittum henni beint á báða netþjónana sama dag, og skráðum hana í útgáfustillingarnar svo næsta útgáfa setti ekki 384 aftur án þess að segja neitt. Lagfæring sem gerð er með hendi á netþjóni sem næsta útgáfa afturkallar er algeng leið til að sama útfall komi aftur.
Meira minni var skynsamlegt öryggisbil. Það var ekki skýring.
Raunverulega orsökin: ein beiðni, 71 MB
Seinna sama dag endurskópum við hrunið á afriti af einu spjalli, með gömlu minnismörkum framleiðslunnar. Afritið hrundi API-inu á eigin spýtur, án þátttöku útgáfu.
Þegar þú opnar verkefni biður vefforritið API-ið um sögu spjallsins. Endapunkturinn skilaði nýjustu 30 skilaboðunum heilum. Fyrir flest spjöll eru 30 skilaboð lítil. Fyrir þetta spjall var nýjustu 30 skilaboðin 53 MB í gagnagrunninum og 71 MB í svari. Nokkur svör agentsins voru 4–10 MB hvert, aðallega afrit undiragenta: hjálparagentarnir sem aðalagentinn ræsir til að vinna hluta verkefnis samhliða, og allt vinnuafrit þeirra er vistað með svarinu.
Að smíða það eina svar tók meira haug en API-ið hafði. Og hver opnun eða endurhleðsla á verkefninu sendi beiðnina aftur. Eitt spjall gat slegið út hvaða netþjón sem það lenti á, og þar sem síðuna mátti endurhlaða gat það náð á báða.
„30 skilaboð“ höfðu aldrei verið raunveruleg stærðarmörk. Þau voru fjöldamörk sem voru tilviljun lítil fyrir venjuleg spjöll.
Lagfæringin: síður takmarkaðar bæði eftir stærð og fjölda
Síða af sögu er nú 30 skilaboð eða um 4 MB af þeim, hvort sem kemur á undan.
Hún virkar í tveimur skrefum:
- Biðja fyrst um stærðir. API-ið biður gagnagrunninn um auðkenni og geymdar stærðir nýjustu skilaboðanna, án innihalds þeirra. Sú fyrirspurn er ódýr sama hversu stór skilaboðin eru.
- Hlaða aðeins því sem kemst fyrir. Með því að ganga frá nýjustu til elstu bætir það við skilaboðum þar til það næsta færi síðuna yfir 4 MB. Aðeins þær raðir eru þá hlaðnar í heild.
Tvær reglur halda þessu öruggu:
- Nýjustu skilaboðin fara alltaf á síðuna, hver sem stærðin er. Síða sem skilar engu myndi skilja spjallið eftir fast, án leiðar til að skruna til baka.
- Engu er skorið. Engin skilaboð eru stytt eða sleppt. Restin af spjallinu hleðst í gegnum stjórnina „Skrunaðu upp fyrir eldri skilaboð“ sem eldri skilaboð notuðu þegar, síðu fyrir síðu.
Við fundum einn stað í viðbót með sama mynstri. Þegar þú stöðvar lotu athugar API-ið nokkur síðustu svörin til að sjá hvort einhver séu tóm og ætti að hreinsa þau. Það gerði það með því að hlaða átta heilum skilaboðum. Í spjalli með margra megabæta svörum er það sama vandamál í minni mynd. Nú spyr það gagnagrunninn já-eða-nei spurningar — hefur þetta skilaboð nokkurt innihald? — án þess að hlaða innihaldinu sjálfu.
Hvernig við prófuðum það
Á afriti spjallsins, keyrandi með gömlu 384 MiB mörkunum:
- Fyrsta síðan er 5 MB. Áður hrundi API-ið á sömu beiðni.
- Fimm vafraflipar sem opna spjallið í einu ná hámarki í 154 MB minni.
- Að skruna til baka í gegnum allt spjallið skilar öllum 190 skilaboðum, hverju einu einu sinni, í röð.
Rökfræðin við val á síðum hefur einnig einingapróf, þar á meðal eitt byggt á raunverulegum stærðum nýjustu skilaboða þess spjalls. Fyrsta síðan kemur til baka með einu skilaboði, segir að fleiri séu til, og næsta síða byrjar á því næsta.
Það sem við lærðum
- Fjöldi er ekki stærðarmörk. Sérhver endapunktur sem skilar „nýjustu N“ af einhverju sem getur vaxið án takmarka þarf líka bætmörk. Gögnin finna bilið fyrr eða síðar.
- Augljósi grunaði er ekki alltaf orsökin. Útgáfan var raunveruleg kveikja að aukavinnu, og meira minni var þess virði. En hrunið endurtókst án útgáfu, og án þess að endurskapa það hefðum við stoppað við minnisaukninguna.
- Tveir netþjónar falla saman þegar þeir fá sömu beiðnina. Varaleið verndar gegn því að einn netþjónn deyi. Hún verndar ekki gegn beiðni sem drepur hvaða netþjón sem hún nær til, sérstaklega þeirri sem notandi endurreynir náttúrulega með því að endurhlaða.
- Lagaðu það í stillingunum, ekki bara á kassanum. Minnisaukningin fór í útgáfustillingarnar sama dag, svo næsta útgáfa hélt henni.
Þetta er önnur nýlega minnissagan sem tengist úttaki agentsins. Sú fyrsta var stöðumerki sem setti 690 MB af strengjum í biðröð. Ef þú notar undiragenta í Mythex geturðu lesið um hvernig þeir virka í skjölunum.