ஒரு கனமான chat எங்கள் API-ஐ எப்படி செயலிழக்கச் செய்திருக்கக்கூடும்
செப்டம்பர் 28 அன்று, ஒரு வெளியீட்டுக்குப் பின் சுமார் 17 நிமிடங்களில், எங்கள் இரண்டு production API சேவையகங்களும் ஒரே நேரத்தில் memory தீர்ந்து மறுதொடங்கின; சுமார் ஐந்து நிமிடங்களுக்கு load balancer-க்கு ஆரோக்கியமான ஒரு சேவையகமும் இல்லை. எங்கள் முதல் சரிசெய்தல் API-க்கு இருமடங்கு memory கொடுத்தது. உண்மைக் காரணம் அன்று பின்னர் வெளிப்பட்டது: ஒரு குறிப்பிட்ட chat-ஐத் திறக்கும்போது API ஒரே request-இல் 53 MB செய்திகளை ஏற்றக் கேட்டது. Chat வரலாறு இப்போது எண்ணிக்கையாலும் அளவாலும் வரம்பிடப்பட்ட பக்கங்களில் ஏற்றப்படுகிறது; அந்த chat செயலிழப்புக்குப் பதிலாக 5 MB முதல் பக்கத்துடன் திறக்கிறது.
என்ன நின்றது
எங்கள் API ஒவ்வொரு இரண்டு சேவையகங்களிலும் ஒரு container-ஆக, ஒரு load balancer-க்குப் பின்னால் இயங்குகிறது. ஒவ்வொரு container-க்கும் 384 MiB memory வரம்பு இருந்தது. ஓய்வில் அது 130–140 MiB பயன்படுத்துகிறது, ஆகவே வரம்பு நிறைய இடம் இருப்பதாகத் தோன்றியது.
இரண்டு containers-உம் ஒரே நேரத்தில் அந்த வரம்பை எட்டி “JavaScript heap out of memory” என்று செயலிழந்தன. Heap என்பது Node.js செயல்முறை தன் பொருட்களுடன் வேலை செய்யும் memory. இரண்டு சேவையகங்களும் ஒரே நேரத்தில் தோல்வியடைந்ததால், request-களை அனுப்ப எங்கும் இடமில்லை. இரண்டு சேவையக அமைப்புக்கு அது மிக மோசமான செயலிழப்பு: ஒரே நேரத்தில் தோல்விகள் நடக்காத வரைதான் இரட்டைமை உதவுகிறது.
முதல் சரிசெய்தல்: கூடுதல் இடம்
நேரம் வெளியீட்டைச் சுட்டிக்காட்டியது. ஒரு வெளியீட்டுக்குப் பின், வெளியீட்டால் குறுக்கிடப்பட்ட agent turns மீண்டும் தொடங்கப்பட்டு அவற்றின் எழுத்துப்பதிவுகளை மீண்டும் சேமிக்கின்றன; அந்த வேலைப் பெருக்கம்தான் memory உயர்வுக்கு வெளிப்படையான காரணமாகத் தெரிந்தது.
ஆகவே முதல் மாற்றம் API வரம்பை 384 MiB-இலிருந்து 768 MiB-ஆக உயர்த்தியது. அதை அன்றே இரண்டு சேவையகங்களிலும் நேரடியாகப் பயன்படுத்தினோம், அடுத்த வெளியீடு அதை 384-க்கு மீண்டும் மாற்றாமல் இருக்க deploy அமைப்பிலும் பதிவு செய்தோம். கைமுறையாக ஒரு சேவையகத்தில் செய்த சரிசெய்தலை அடுத்த deploy அழித்துவிடுவது, அதே செயலிழப்பு திரும்பி வருவதற்கான பொதுவான வழி.
கூடுதல் memory நியாயமான பாதுகாப்பு எல்லை. ஆனால் அது ஒரு விளக்கம் அல்ல.
உண்மையான காரணம்: ஒரு request, 71 MB
அன்று பின்னர், பழைய memory வரம்புடன் ஒரு chat-இன் நகலில் செயலிழப்பை மீண்டும் உருவாக்கினோம். அந்த நகல் deploy இல்லாமலேயே API-ஐத் தனியாகச் செயலிழக்கச் செய்தது.
ஒரு project-ஐத் திறக்கும்போது, web app அந்த chat-இன் வரலாற்றை API-யிடம் கேட்கிறது. Endpoint சமீபத்திய 30 செய்திகளை முழுமையாகத் திருப்பியது. பெரும்பாலான chats-க்கு 30 செய்திகள் சிறியவை. இந்த chat-க்கு, சமீபத்திய 30 செய்திகள் database-இல் 53 MB; response 71 MB. Agent-இன் சில பதில்கள் ஒவ்வொன்றும் 4–10 MB, பெரும்பாலும் sub-agents-இன் எழுத்துப்பதிவுகள்: முக்கிய agent ஒரு பணியின் பகுதியை இணையாகச் செய்ய தொடங்கும் உதவி agents; அவற்றின் முழு வேலை பதிலுடன் சேமிக்கப்படுகிறது.
அந்த ஒரு response-ஐக் கட்டுவதே API-இடம் இருந்ததைவிட அதிக heap எடுத்தது. ஒவ்வொரு திறப்பிலும் அல்லது refresh-இலும் அந்தத் திட்டத்துக்கான request மீண்டும் அனுப்பப்பட்டது. ஒரே chat எந்தச் சேவையகத்தை அடைந்ததோ அதைச் செயலிழக்கச் செய்யும்; பக்கத்தை மீண்டும் ஏற்றலாம் என்பதால் இரண்டையும் அடையக்கூடும்.
“30 செய்திகள்” அளவின் மீதான உண்மையான வரம்பாக ஒருபோதும் இருந்ததில்லை. அது சாதாரண chats-க்கு சிறியதாக இருந்த எண்ணிக்கை வரம்பு மட்டுமே.
தீர்வு: எண்ணிக்கை மட்டுமல்ல, அளவாலும் வரம்பிட்ட பக்கங்கள்
வரலாற்றுப் பக்கம் இப்போது 30 செய்திகள் அல்லது சுமார் 4 MB, எது முதலில் வருகிறதோ அது.
இது இரண்டு படிகளில் வேலை செய்கிறது:
- முதலில் அளவுகளைக் கேளுங்கள். API, சமீபத்திய செய்திகளின் IDs-ஐயும் சேமிக்கப்பட்ட அளவுகளையும் உள்ளடக்கம் இல்லாமல் database-இடம் கேட்கிறது. அந்த query செய்திகள் எவ்வளவு பெரியவையாக இருந்தாலும் மலிவானது.
- பொருந்துவதை மட்டும் ஏற்றுங்கள். சமீபத்தியதிலிருந்து பழையதை நோக்கி நடந்து, அடுத்த செய்தி பக்கத்தை 4 MB-ஐத் தாண்டும் வரை செய்திகளைச் சேர்க்கிறது. அந்த வரிசைகள் மட்டுமே முழுமையாக ஏற்றப்படுகின்றன.
இரண்டு விதிகள் அதைப் பாதுகாப்பாக வைக்கின்றன:
- சமீபத்திய செய்தி அதன் அளவு எதுவாக இருந்தாலும் எப்போதும் பக்கத்தில் வரும். எதையும் திருப்பாத பக்கம் chat-ஐ சிக்க வைக்கும்; பின்னோக்கி உருட்ட வழி இருக்காது.
- எதுவும் வெட்டப்படுவதில்லை. எந்தச் செய்தியும் சுருக்கப்படுவதோ கைவிடப்படுவதோ இல்லை. மீதி chat ஏற்கனவே பழைய செய்திகளுக்காகப் பயன்படுத்தப்பட்ட “Scroll up for earlier messages” கட்டுப்பாட்டின் மூலம், ஒரு நேரத்தில் ஒரு பக்கமாக ஏற்றப்படுகிறது.
அதே வடிவம் இன்னொரு இடத்திலும் இருந்தது. நீங்கள் ஒரு turn-ஐ நிறுத்தும்போது, API கடைசி சில பதில்கள் காலியாக உள்ளனவா, சுத்தம் செய்ய வேண்டுமா என்று பார்க்கிறது. அது அதற்காக எட்டு முழுச் செய்திகளை ஏற்றியது. பல-மெகாபைட் பதில்கள் உள்ள chat-இல் அது அதே பிரச்சனையின் சிறிய வடிவம். இப்போது உள்ளடக்கத்தை ஏற்றாமல், database-இடம் ஒரு ஆம்-இல்லை கேள்வி கேட்கிறது: இந்தச் செய்தியில் ஏதாவது உள்ளடக்கம் உள்ளதா?
இதை எப்படிச் சரிபார்த்தோம்
அந்த chat-இன் நகலில், பழைய 384 MiB வரம்புடன் இயக்கி:
- முதல் பக்கம் 5 MB. முன்பு அதே request API-ஐச் செயலிழக்கச் செய்தது.
- அந்த chat-ஐ ஒரே நேரத்தில் திறக்கும் ஐந்து browser tabs உச்சத்தில் 154 MB memory.
- முழு chat-ஐயும் பின்னோக்கி உருட்டினால், 190 செய்திகளும் ஒவ்வொன்றும் ஒரு முறை, வரிசைப்படி திரும்புகின்றன.
பக்கத் தேர்வு logic-க்கும் அலகுச் சோதனைகள் உள்ளன; அவற்றில் ஒன்று அந்த chat-இன் சமீபத்திய செய்திகளின் உண்மையான அளவுகளிலிருந்து கட்டப்பட்டது. முதல் பக்கம் ஒரு செய்தியுடன் திரும்புகிறது, இன்னும் உள்ளன என்று சொல்கிறது, அடுத்த பக்கம் அதற்கு அடுத்ததிலிருந்து தொடங்குகிறது.
நாங்கள் கற்றது
- எண்ணிக்கை என்பது அளவு வரம்பு அல்ல. அளவிட முடியாமல் வளரக்கூடிய ஏதோ ஒன்றின் “சமீபத்திய N” என்று திருப்பும் எந்த endpoint-க்கும் ஒரு byte பட்ஜெட்டும் தேவை. தரவு இறுதியில் அந்த இடைவெளியைக் கண்டுபிடிக்கும்.
- வெளிப்படையான சந்தேகம் எப்போதும் காரணமாக இருப்பதில்லை. Deploy உண்மையான தூண்டுதல்தான், கூடுதல் memory பயனுள்ளதுதான். ஆனால் deploy இல்லாமலும் செயலிழப்பு மீண்டும் உருவானது; அதை மீண்டும் உருவாக்காமல் இருந்திருந்தால், memory உயர்வில் நின்றிருப்போம்.
- ஒரு request இரண்டு சேவையகங்களையும் அடைந்தால், இரண்டும் ஒன்றாக தோல்வியடைகின்றன. ஒரு சேவையகம் இறப்பதற்கு எதிராக இரட்டைமை பாதுகாக்கும். எந்த சேவையகத்தை அடைந்தாலும் அதைக் கொல்லும் request-க்கு எதிராக அது பாதுகாக்காது — குறிப்பாக பயனர் பக்கத்தை refresh செய்து மீண்டும் முயற்சிப்பார் என்றால்.
- அதை config-இல் சரிசெய்யுங்கள், பெட்டியில் மட்டும் அல்ல. Memory உயர்வு அதே நாளில் deploy அமைப்புக்குள் சென்றது; அதனால் அடுத்த deploy அதைத் தக்கவைத்தது.
Sub-agents-ஐ Mythex-இல் பயன்படுத்தினால், அவை எப்படி வேலை செய்கின்றன என்பதை docs-இல் படிக்கலாம். இது agent-இன் வெளியீடு தொடர்பான இரண்டாவது சமீபத்திய நினைவகக் கதை. முதலாவது ஒரு progress label 690 MB string-களை வரிசையில் வைத்த கதை.