Blog

Hogyan tudott egy nehéz chat leállítani az API-nkat

Szeptember 28-án, egy kiadás után kb. 17 perccel, mindkét éles API-szerverünk egyszerre fogyott ki a memóriából, újraindult, és körülbelül öt percig a terheléselosztónak nem volt egészséges szervere, amelynek forgalmat küldhetett volna. Az első javításunk az API-nak a kétszeres memóriát adta. Az igazi ok később, aznap derült ki: egy konkrét chat megnyitása egyetlen kérésben 53 MB üzenet betöltését kérte az API-tól. Az előzménylapok most a darabszám mellett mérettel is korlátozottak, és ez a chat 5 MB-os első oldallal nyílik, nem összeomlással.

Mi állt le

Az API konténerként fut mindkét szerveren, a terheléselosztó mögött. Minden konténer memóriakorlátja 384 MiB volt. Nyugalmi állapotban 130–140 MiB-ot használ, így a korlát bőven elegendőnek tűnt.

Mindkét konténer egyszerre érte el a korlátot, és „JavaScript heap out of memory” hibával összeomlott. A heap az a memória, amelyet egy Node.js folyamat az általa használt objektumokhoz foglal. Mivel mindkét szerver egyszerre hibázott, nem maradt, ahová a kéréseket irányítani lehetett volna. Ez a legrosszabb fajta kiesés egy kétszerveres felállásban: a redundancia csak akkor segít, ha a hibák nem egyszerre jönnek.

Az első javítás: több hely

Az időzítés a kiadásra mutatott. Egy kiadás után a deploy által megszakított ügynöki körök folytatódnak, és az átiratukat újra mentik, és ez a munkahullám nyilvánvaló forrásnak tűnt egy memóriacsúcsra.

Az első változtatás ezért 384 MiB-ról 768 MiB-ra emelte az API korlátját. Ugyanazon a napon élesben alkalmaztuk mindkét szerveren, és a deploy-konfigurációba is beírtuk, hogy a következő deploy ne állítsa csendben vissza 384-re. Egy kézzel, szerveren végzett javítás, amelyet a következő deploy visszacsinál, gyakori módja annak, hogy ugyanaz a kiesés visszatérjen.

A nagyobb memória ésszerű biztonsági tartalék volt. Nem magyarázat.

Az igazi ok: egy kérés, 71 MB

Később aznap egy chat másolatán reprodukáltuk az összeomlást, az éles környezet régi memóriakorlátjával. A másolat önmagában összeomlasztotta az API-t, deploy nélkül.

Amikor megnyitsz egy projektet, a webalkalmazás lekéri az API-tól a chat előzményeit. Az endpoint a legutóbbi 30 üzenetet adta vissza, teljesen. A legtöbb chatnél 30 üzenet kicsi. Ennél a chatnél a legutóbbi 30 üzenet 53 MB volt az adatbázisban, és 71 MB volt a válasz. Az ügynök több válasza 4–10 MB volt egyenként, főleg al-ügynökök átiratai: a segéd-ügynökök, amelyeket a fő ügynök a feladat egy részének párhuzamos elvégzésére indít, és teljes munkájuk a válasszal együtt van mentve.

Ezt az egyetlen választ összeállítani több heapet igényelt, mint amennyi az API-nak volt. És a projekt minden megnyitása vagy frissítése újraküldte a kérést. Egyetlen chat bármelyik szervert, amelyre került, leállíthatta, és mivel az oldal újratölthető, mindkettőt elérhette.

A „30 üzenet” sosem volt valódi mérethatár. Darabszámhatár volt, amely a normál chatekhez épp kicsi volt.

A javítás: méret szerint is korlátozott oldalak

Az előzmények egy oldala most 30 üzenet vagy kb. 4 MB, attól függően, melyik teljesül előbb.

Két lépésben működik:

  1. Előbb méretet kérünk. Az API az adatbázistól a legutóbbi üzenetek azonosítóit és tárolt méretét kéri, a tartalmuk nélkül. Ez a lekérdezés olcsó, bármilyen nagyok is az üzenetek.
  2. Csak azt töltjük be, ami belefér. A legújabbtól a legrégebbi felé haladva üzeneteket veszünk hozzá, amíg a következő túllépné a 4 MB-ot az oldalon. Csak azokat a sorokat töltjük be teljesen.

Két szabály biztosítja, hogy ez biztonságos legyen:

  • A legújabb üzenet mindig az oldalra kerül, bármekkora is. Egy üres oldal beragadt chatet eredményezne, ahonnan nem lehetne visszagörgetni.
  • Semmi nincs levágva. Egyetlen üzenet sincs csonkítva vagy eldobva. A chat többi része a „Görgess fel a korábbi üzenetekért” gombon keresztül töltődik be, oldalanként, ahogy a régebbi üzenetek eddig is.

Egy másik helyen is ugyanez a minta volt. Amikor leállítasz egy kört, az API ellenőrzi az utolsó néhány választ, hogy van-e köztük üres, amelyet tisztítani kell. Ehhez nyolc teljes üzenetet töltött be. Több megabájtos válaszokból álló chaten ez a probléma kisebb formája. Most az adatbázisnak egy igen-nem kérdést tesz fel — van ennek az üzenetnek tartalma? —, anélkül, hogy magát a tartalmat betöltené.

Hogyan ellenőriztük

A chat másolatán, a régi 384 MiB-os korláttal:

  • Az első oldal 5 MB. Korábban ugyanez a kérés összeomlasztotta az API-t.
  • Öt böngészőlap, amely egyszerre nyitja meg a chatet, 154 MB csúcsmemóriát használ.
  • A teljes chat visszagörgetése mind a 190 üzenetet visszaadja, mindegyik pontosan egyszer, sorrendben.

Az oldalválasztó logikának egységtesztjei is vannak, köztük egy, amely a chat legutóbbi üzeneteinek valódi méreteiből épül. Az első oldalon egy üzenet jön vissza, jelzi, hogy van több, a következő oldal pedig a rákövetkező üzenettel kezdődik.

Mit vittünk ebből magunkkal

  • A darabszám nem méretkorlát. Minden végpont, amely „a legújabb N” elemet ad vissza egy olyan dologból, amely korlátlanul nőhet, bájtkeretet is igényel. Az adat előbb-utóbb megtalálja a rést.
  • A kézenfekvő gyanúsított nem mindig az ok. A deploy valódi kiváltó tényező volt a többletmunkához, és a több memória megérte. De az összeomlás deploy nélkül is reprodukálódott, és reprodukció nélkül a memórianövelésnél megálltunk volna.
  • Két szerver ugyanarra a kérésre együtt bukik. A redundancia egy szerver elhalása ellen véd. Egy olyan kérés ellen nem véd, amely bármelyik szervert, amelyet elér, megöli — főleg, ha a felhasználó természetes módon frissítéssel próbálkozik újra.
  • A konfigban javíts, ne csak a gépen. A memórianövelés még aznap bekerült a deploy-konfigurációba, így a következő deploy megtartotta.

Ez a második közelmúltbeli memóriatörténetünk, amely az ügynök kimenetét érinti. Az első egy állapotjelző címke, amely 690 MB stringet sorolt be. Ha al-ügynököket használsz a Mythexen, a dokumentációban olvashatsz arról, hogyan működnek.

Mythex · Pricing · Építs bármit AI-val · Weboldal- és alkalmazássablonok · A Mythex összehasonlítva · AI-alapú alkalmazásépítési szószedet · Ingyenes eszközök AI-alapú alkalmazásépítéshez · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)