Бір ауыр чат біздің API-ді қалай құлатуы мүмкін еді
28 қыркүйекте, релиздан шамамен 17 минут өткенде, өндірістегі екі API серверіміз бір уақытта жадтың таусылуынан құлап, қайта іске қосылды, ал шамамен бес минут бойы жүктеме теңестірушісінің жіберуге сау сервері болмады. Алғашқы түзету API-ге екі есе көбірек жад берді. Нақты себеп сол күннің соңында шықты: белгілі бір чатты ашу API-ден бір сұраныста 53 МБ хабарлама жүктеуді талап етті. Чат тарихы енді санына ғана емес, өлшеміне де шектелген беттермен жүктеледі, ал сол чат 5 МБ-лық бірінші бетпен құлаудың орнына ашылады.
Не болды
API-міз екі серверге қойылған теңестіруші артындағы контейнер ретінде жұмыс істейді. Әр контейнердің жад шегі 384 МиБ болатын. Тыныш күйде ол 130–140 МиБ пайдаланады, сондықтан шек жеткілікті көрінді.
Екі контейнер бір уақытта шекке жетіп, «JavaScript heap out of memory» деген хабармен құлады. Heap — Node.js процесі жұмыс істейтін объектілерге арналған жады. Екі сервер бір уақытта құлағандықтан, сұраныстарды жіберетін жер қалмады. Екі серверлік орнату үшін бұл ең нашар үзіліс түрі: қайталау тек бір сервер құлағанда ғана көмектеседі, ал ақаулар бір уақытта болса, көмектеспейді.
Алғашқы түзету: көбірек орын
Уақыт релизге нұсқады. Релизден кейін релиз үзген агент жауаптары қайта жалғастырылып, транскрипттерін қайта сақтайды, және сол жұмыс жадтың секірісінің анық көзі сияқты көрінді.
Сондықтан алғашқы өзгеріс API шегін 384 МиБ-тан 768 МиБ-қа көтерді. Оны сол күні екі серверде де тікелей қолдандық және келесі деплой 384-ті үнсіз қайтармауы үшін деплой конфигурациясына да енгіздік. Серверде қолмен жасалған, келесі деплой қайтаратын түзету — бірдей үзілістің қайта оралуының кең таралған жолы.
Көбірек жад — жақсы қорғаныс маржасы болды. Бірақ ол түсіндірме емес еді.
Нақты себеп: бір сұраныс, 71 МБ
Сол күннің соңында біз ескі жад шегімен жұмыс істеп тұрған чаттың көшірмесінде құлауды қайта жасадық. Көшірме API-ді деплойсыз-ақ өзінше құлатты.
Жобаны ашқанда веб-қолданба API-ден чаттың тарихын сұрайды. Эндпоинт ең жаңа 30 хабарламаны толығымен қайтаратын. Көп чаттар үшін 30 хабарлама аз. Бұл чатта ең жаңа 30 хабарлама дерекқорда 53 МБ, жауап ретінде 71 МБ болды. Агенттің кейбір жауаптары 4–10 МБ болды, көбіне суб-агенттердің транскрипттері: негізгі агент тапсырманың бір бөлігін параллель орындау үшін іске қосатын көмекші агенттер, олардың толық жұмысы жауаппен бірге сақталады.
Осы жауапты құрастыру API-де бар жадтан көбірек heap алды. Әр жобаны ашу немесе жаңарту осы сұранысты қайта жіберді. Бір чат кез келген серверді құлата алды, ал бет қайта жүктелетіндіктен, ол екеуіне де жете алды.
«30 хабарлама» ешқашан өлшемнің нақты шегі болмаған. Ол санның шегі еді, ал қалыпты чаттар үшін ол шағын болып келді.
Түзету: санмен қатар өлшем бойынша беттер
Тарих беті енді 30 хабарлама немесе шамамен 4 МБ — қайсысы бірінші келсе, соны құрайды.
Ол екі қадамда жұмыс істейді:
- Алдымен өлшемдерді сұраңыз. API дерекқордан ең жаңа хабарламалардың ID-лері мен сақталған өлшемдерін мазмұнсыз сұрайды. Бұл сұрау хабарламалар қандай үлкен болса да арзан.
- Тек сыйғанын жүктеңіз. Ең жаңадан ең ескіге қарай жүріп, келесі хабарлама бетті 4 МБ-тан асыра алатын жерге дейін қосады. Тек сол жолдар толық жүктеледі.
Екі ереже қауіпсіздікті сақтайды:
- Ең жаңа хабарлама әрқашан бетке кіреді, оның өлшемі қандай болса да. Бос қайтарған бет чатты тығындап, тарихқа қайта оралу мүмкіндігін жояды.
- Ештеңе кесілмейді. Ешқандай хабарлама қысқартылмайды немесе жойылмайды. Қалған чат «Ескі хабарламаларды көру үшін жоғары айналдырыңыз» басқару элементі арқылы жүктеледі, ескі хабарламалар бұрыннан солай жүктелетін, бір бетпен.
Тағы бір жерді таптық, ұқсас пішінде. Жауапты тоқтатқанда API соңғы бірнеше жауаптың бос екенін тексеріп, тазарту керек пе соны анықтайды. Ол бұған дейін сегіз толық хабарламаны жүктеп жасайтын. Көп мегабайтты жауаптары бар чатта бұл сол мәселенің кішірек түрі. Енді ол дерекқордан бір иә-жоқ сұрағын сұрайды — бұл хабарламада мазмұн бар ма? — мазмұнның өзін жүктемей.
Қалай тексердік
Чаттың көшірмесінде, ескі 384 МиБ шегімен жұмыс істегенде:
- Бірінші бет — 5 МБ. Бұрын дәл осы сұраныс API-ді құлатқан.
- Бір уақытта ашылған бес браузер қойындысы жадта 154 МБ шыңына жетеді.
- Чаттың барлық тарихына жоғары айналдырғанда барлық 190 хабарлама қайтады, әрқайсысы бір рет, ретімен.
Бет таңдау логикасының да бірлік тесттері бар, соның ішінде осы чаттың ең жаңа хабарламаларының нақты өлшемдерінен құрылған тест. Бірінші бет бір хабарлама қайтарады, одан басқалары бар екенін айтады, ал келесі бет соның келесісінен басталады.
Біз не үйрендік
- Санақ — өлшем шегі емес. «Ең жаңа N» қайтаратын, шексіз өсе алатын кез келген эндпоинт байт бюджетін де қажет етеді. Деректер ақыр соңында кемшілікті табады.
- Ең айқын күдікті әрдайым себеп емес. Деплой нақты іске қосқыш болды және көбірек жад пайдалы болды. Бірақ құлау деплойсыз қайталанды, және оны қайталамасақ, жадты көбейтумен тоқтап қалар едік.
- Екі сервер бір сұранысты алғанда бірге құлайды. Резервтеу бір сервердің өлуінен қорғайды. Ол әр серверді өлтіретін сұраныстан қорғамайды, әсіресе пайдаланушы бетті жаңарту арқылы қайта жіберетін сұраныстан.
- Түзетуді конфигурацияға енгізіңіз, тек қорапқа емес. Жад өсімі сол күні деплой конфигурациясына енді, сондықтан келесі деплой оны сақтады.
Бұл агенттің шығысына қатысты біздің екінші жақындағы жад оқиғамыз. Бірінші оқиға — прогресс жапсырмасы 690 МБ жолды кезекке қойды. Mythex-те суб-агенттерді пайдаланасаңыз, олардың қалай жұмыс істейтінін құжаттардан оқи аласыз.