मेरे पास एक इलीक्सिर/ओटीपी एप्लीकेशन है जो आउट-ऑफ-मेमोरी समस्या के कारण उत्पादन में क्रैश होता है। क्रैश का कारण बनने वाला कार्य एक समर्पित प्रक्रिया में हर 6 घंटे कहलाता है। यह कई मिनट (~ 30) लेता है चलाने के लिए और इस तरह दिखता है:बड़ी बाइनरी लीक को हल करना
def entry_point do
get_jobs_to_scrape()
|> Task.async_stream(&scrape/1)
|> Stream.map(&persist/1)
|> Stream.run()
end
अपने स्थानीय मशीन पर मैं बड़े बाइनरी स्मृति की खपत में लगातार वृद्धि देख समारोह चलता है जब:
ध्यान दें कि जब मैं उस प्रक्रिया पर कचरा संग्रह मैन्युअल रूप से ट्रिगर करता हूं जो फ़ंक्शन चलाता है तो मेमोरी खपत में काफी गिरावट आती है, इसलिए यह निश्चित रूप से जीसी में असमर्थ कई अलग-अलग प्रक्रियाओं में कोई समस्या नहीं है, लेकिन केवल एक ही जीसी सही नहीं है। इसके अलावा, यह कहना महत्वपूर्ण है कि प्रत्येक कुछ मिनट प्रक्रिया जीसी का प्रबंधन करती है, लेकिन कभी-कभी यह पर्याप्त नहीं है। उत्पादन सर्वर में केवल 1 जीबी रैम है और यह जीसी के आने से पहले दुर्घटनाग्रस्त हो जाता है।
इस समस्या को हल करने की कोशिश कर रहा था, मैं Erlang in Anger (पेज 66-67 देखें) में आया था। एक सुझाव है कि सभी बंद बाइनरी मैनिप्लेशंस को एक-ऑफ प्रक्रियाओं में डालना है। scrape
फ़ंक्शन का वापसी मूल्य एक मानचित्र है जिसमें बड़ी बाइनरीयां होती हैं। इसलिए, वे Task.async_stream
"श्रमिकों" और प्रक्रिया को चलाने वाली प्रक्रिया के बीच साझा किए जाते हैं। तो, सिद्धांत रूप में, मैं persist
10 के अंदर scrape
के साथ persist
डाल सकता था। मैं ऐसा नहीं करना पसंद करता हूं, और प्रक्रिया को persist
पर प्रक्रिया के माध्यम से सिंक्रनाइज़ करता हूं।
एक और सुझाव समय-समय पर :erlang.garbage_collect
पर कॉल करना है। ऐसा लगता है कि यह समस्या हल करता है लेकिन रास्ता बहुत हैकी लगता है। लेखक भी इसकी सिफारिश नहीं करता है।
def entry_point do
my_pid = self()
Task.async(fn -> periodically_gc(my_pid) end)
# The rest of the function as before...
end
defp periodically_gc(pid) do
Process.sleep(30_000)
if Process.alive?(pid) do
:erlang.garbage_collect(pid)
periodically_gc(pid)
end
end
और परिणामस्वरूप स्मृति लोड:
मैं काफी कैसे पुस्तक में अन्य सुझावों समस्या फिट समझ में नहीं आता यहाँ मेरे वर्तमान समाधान है।
आप उस मामले में क्या अनुशंसा करेंगे? हैकी समाधान रखें या बेहतर विकल्प हैं।
आप ईटीएस में बाइनरी रखने पर विचार किया था:
इस विषय पर एक अच्छा पढ़ा है? यदि आप उन्हें विश्वसनीय रूप से रिलीज़ कर सकते हैं, तो प्रभावी ढंग से आप मैन्युअल आवंटन करने के लिए आगे बढ़ते हैं और बीएएम के जीसी में किसी भी तरह की नीचता को बाधित करते हैं। ओटीओएच, अगर यह एक उचित हिमपात का टुकड़ा है, तो मैन्युअल कॉल-जीसी समाधान पर्याप्त है? – cdegroot
दिलचस्प विचार! चलो देखते हैं कि मैं समझता हूं: स्क्रैपर फ़ंक्शन होने से डेटा को ईटीएस में रखा जाता है और फिर तालिका में डेटा पर 'persist' फ़ंक्शन को मैप किया जाता है, क्या यह सही है? फिर भी, 'Task.async_stream' कर्मचारी के पास बड़ी बाइनरी के संदर्भ होंगे, और मुख्य प्रक्रिया में' persist 'फ़ंक्शन के अंदर ईटीएस से लाने के लिए समान संदर्भ होंगे, और समस्या बनी रहेगी। – Nagasaki45
ठीक है, "प्रत्येक गणना करने योग्य आइटम को कार्य के लिए तर्क के रूप में पारित किया जाता है और अपने कार्य द्वारा संसाधित किया जाता है" 'Task.async_stream' दस्तावेज़ के अनुसार, इसलिए यदि आपके प्रत्येक 'स्क्रैप' कॉल केवल बाइनरी का निर्माण करते हैं, तो इसे ईटीएस में दबाएं , और एक संदर्भ वापस, तो आप व्यापार में हो सकता है। – cdegroot