Copilot की memory leak समस्या
(stevenharman.net)ActiveSupport::Notifications से जुड़ी Memory Leak को हल करने की प्रक्रिया का सार
-
वह स्थिति जिसमें Memory Leak हुआ
- एक निश्चित समय के बाद
webDyno की memory usage असामान्य रूप से बढ़ने लगी - Pager बजने लगा, और स्थिति memory leak जैसी दिखी
- एक निश्चित समय के बाद
-
तत्काल प्रतिक्रिया
- Heroku में यदि memory leak का संदेह हो, तो Dyno को restart करके अस्थायी समाधान किया जा सकता है
- सामान्य deploy cycle के अनुसार restart करना, या memory limit के करीब पहुँचे Dyno को मैन्युअली restart करना
-
कारण पता लगाने के लिए संदिग्ध कोड की समीक्षा
- memory spike से ठीक पहले deploy किए गए code changes की समीक्षा
- कारण माने जा रहे कुछ कोड को एक-एक करके deploy कर यह जाँचना कि memory leak होता है या नहीं
- ऐसा कोई code नहीं मिला जो कारण लगता हो, इसलिए tooling changes भी rollback करके जाँचे गए। फिर भी memory leak जारी रहा
-
memory बढ़ने के pattern का विश्लेषण
- leak सिर्फ
webDyno में हुआ। Sidekiq, Delayed::Job Dyno सामान्य थे - सभी
webDyno हमेशा leak नहीं कर रहे थे। कुछ घंटे सामान्य उपयोग के बाद एक-दो या सभी Dyno में leak शुरू हो जाता था - ट्रैफ़िक की मात्रा की बजाय किसी विशेष ट्रैफ़िक के कारण यह हो रहा है, ऐसा संदेह हुआ
- Dyno के भीतर सभी Puma worker में leak नहीं हो रहा था; कुछ ही worker कुल memory का अधिकांश हिस्सा उपयोग कर रहे थे
- leak सिर्फ
-
Heap dump का संग्रह और विश्लेषण
rbtraceका उपयोग करके leak हो रहे Ruby process का heap dump इकट्ठा किया गयाheroku ps:execसे leak वाले dyno में ssh के जरिए प्रवेश किया गयाpsकमांड से सबसे अधिक memory उपयोग कर रहे Ruby worker process का चयन किया गयाrbtraceसे उस pid पर attach करके memory allocation tracing शुरू की गई (ObjectSpace.trace_object_allocations_start)ObjectSpace.dump_allसे heap dump इकट्ठा किया गया। आकार बड़ा होने पर gzip compression किया गयाheroku ps:copyसे dump file को लोकल मशीन पर लाया गया
reapका उपयोग कर heap dump को flamegraph के रूप में visualize किया गया- एक Thread मिला जो 1.9GB memory को reference कर रहा था, और उसके नीचे 32,067 objects को reference करने वाला एक Array मिला
sheapका उपयोग कर संदिग्ध objects की जाँच की गई- वह Thread, Puma का worker thread निकला
ActiveSupport::SubscriberQueueRegistryobject एकHashको reference कर रहा था, और उसके नीचेStringतथाArrayobjects थे- समस्या वाले
Arrayमें 32,000 से अधिकActiveSupport::Notifications::Eventobjects जमा थे
-
कारण के बारे में अनुमान
- अनुमान लगाया गया कि
ActiveSupport::NotificationsकेEventobjects गलत तरीके से#childrenarray में जमा हो रहे हैं - यदि
ActiveSupport::Notifications.instrumentblock के भीतर error हो, तो संबंधितEvent#childrenसे हटे बिना वहीं रह जाता है, और इससे memory leak होता है
- अनुमान लगाया गया कि
-
लोकल में पुनरुत्पादन
- production में मिले संदिग्ध request path और parameter के साथ लोकल में request भेजी गई
500 Internal Server Errorके साथURI::InvalidURIErrorहोने की पुष्टि हुई- यह भी पुष्टि हुई कि वही request भेजने पर production dyno की memory usage तेज़ी से बढ़ जाती है
-
ठोस कारण का विश्लेषण
- Rails 7.1 में ठीक किया गया
ActiveSupport::NotificationsकाEvent#childrenसे जुड़ा एक bug मौजूद था - इसके साथ Bugsnag gem में request url को clean करने की प्रक्रिया के दौरान
URI.parseपरURI::InvalidURIErrorraise होने वाला bug भी जुड़ गया, और memory leak हुआ ActiveSupport::Notifications.subscribeblock के भीतर raise हुई error पकड़ी नहीं गई, इसलिए संबंधितEvent#childrenarray से हटाया नहीं गया और जमा होता गया, जिससे memory leak हुआ
- Rails 7.1 में ठीक किया गया
-
समाधान
- अल्पकालिक: Bugsnag gem को ऐसे version में upgrade करना जहाँ
URI::InvalidURIErrorहोने पर भी error raise न हो - दीर्घकालिक: उस Rails 7.x में upgrade करना जिसमें
ActiveSupport::Notificationsका bug ठीक किया गया है
- अल्पकालिक: Bugsnag gem को ऐसे version में upgrade करना जहाँ
GN⁺ की राय
- समस्या को पहचानकर व्यवस्थित तरीके से उसके कारण तक पहुँचने की प्रक्रिया प्रभावशाली है। memory leak का संदेह होने पर आज़माए जा सकने वाले बुनियादी analysis steps अच्छी तरह संकलित किए गए हैं
- Ruby में heap dump इकट्ठा करने, visualize करने और analyze करने के लिए कई open source tools (
rbtrace,reap,sheapआदि) सक्रिय रूप से विकसित होते दिखते हैं। सिर्फ Ruby ही नहीं, किसी भी भाषा में उपयोगी memory analysis tools को जानना और समस्या पर लागू करना महत्वपूर्ण लगता है - वास्तव में memory leak का कारण अक्सर किसी विशेष library या framework का bug होता है, लेकिन ऐसे bug को सीधे analyze कर patch बनाकर deploy करना हमेशा संभव नहीं होता। इसलिए जितनी जल्दी हो सके workaround लागू करना महत्वपूर्ण है। bug report के साथ व्यावहारिक विकल्प देना भी अच्छा तरीका है
- केवल memory leak ठीक करने पर रुकने के बजाय, समस्या के root cause तक गहराई से जाने का पहलू भी अच्छा लगा। framework के आंतरिक code को ध्यान से देखकर मूल कारण तक पहुँचने वाली विश्लेषणात्मक दृष्टि डेवलपर के लिए आवश्यक लगती है
- अंततः memory leak का कारण एक मामूली library version upgrade निकला, जो शुरुआत में पूरी तरह असंबंधित लगता था। यह dependency management और changes tracking के महत्व को दिखाने वाला उदाहरण है। छोटे बदलावों के प्रभाव का भी सावधानी से विश्लेषण करना और deploy के बाद monitoring करना ज़रूरी है
1 टिप्पणियां
Hacker News राय
मैनुअल मेमोरी मैनेजमेंट के डर के बिना, इसे इंजीनियरिंग ट्रेनिंग से हल किया जा सकता है
मेमोरी लीक की वजह से 50 लाख डॉलर के नुकसान का मामला
मेमोरी लीक debugging tools और समाधान
लेखन शैली की प्रशंसा