メモリ不足の問題が原因で本番でクラッシュするエリクシール/ OTPアプリケーションがあります。クラッシュの原因となる機能は、専用のプロセスで6時間ごとに呼び出されます。これは、実行に数分(〜30)を取り、次のようになります。私のローカルマシン上大きなバイナリのリークを解決する
def entry_point do
get_jobs_to_scrape()
|> Task.async_stream(&scrape/1)
|> Stream.map(&persist/1)
|> Stream.run()
end
を機能の実行時に、私は大きなバイナリメモリ消費量の一定の成長を参照してください。
を関数を実行するプロセスでガベージコレクションを手動で起動すると、メモリ消費量が大幅に減少するため、GCには対応していない複数のプロセスでは問題はありませんが、GCが正しくないプロセスでは問題はありません。さらに、数分おきにプロセスはがGCに管理されますが、時にはそれだけでは不十分だと言うことは重要です。プロダクションサーバーには1GBのRAMしかなく、GCが起動する前にクラッシュします。
問題を解決しようとしましたErlang in Anger(66-67ページを参照)に出くわしました。 1つの提案は、大きなバイナリ操作のすべてを一度のプロセスで行うことです。 scrape
関数の戻り値は、大きなバイナリを含むマップです。したがって、それらは "Task.async_stream
"ワーカー "とその機能を実行するプロセスの間で共有されます。だから理論的にはpersist
をscrape
と一緒にTask.async_stream
に入れることができます。私はそうしないことを好むし、persist
への呼び出しをプロセスを通して同期させておくことを望む。
もう1つの提案は、: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
、得られたメモリ負荷:私はかなりの本の中で他の提案は、問題をどのように適合するか理解していない
ここに私の現在のソリューションです。
この場合、あなたは何をお勧めしますか?ハッキーな解決策を保つか、より良い選択肢があります。
あなたはETSでバイナリを保存することを考えましたか?それらを確実にリリースできる場合は、効果的に手作業による割り当てを行い、BEAMのGCの不快さを回避します。 OTOH、これが適切なスノーフレークであれば、手動コールGCソリューションで十分でしょうか? – cdegroot
興味深いアイデア!私が理解しているかどうかを見てみましょう:スクレーパー機能を使ってデータをETSに入れ、テーブル内のデータに 'persist'関数をマップするのは正しいのですか?それでも、 'Task.async_stream'ワーカーは大きなバイナリへの参照を持ち、メインプロセスは' persist'関数の中でETSからフェッチするための同じ参照を持ち、問題は残っています。 – Nagasaki45
まあ、 "各列挙可能なアイテムは、関数の引数として渡され、独自のタスクで処理されます"ということは、 'Task.async_stream'のドキュメントにあるので、' scrape'コールのそれぞれがバイナリをビルドするだけであれば、ETS 、参照を返すと、あなたはビジネスにいるかもしれません。 – cdegroot