У меня есть приложение elixir/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, но иногда этого недостаточно. Производственный сервер имеет только 1 ГБ оперативной памяти, и он сработает до того, как GC запустится.
Пытаясь решить проблему, я столкнулся с Erlang in Anger (см. страницы 66-67). Одно из предложений - положить все большие манипуляции с бинарниками в одноразовых процессах. Возвращаемое значение функции scrape
- это карта, содержащая большие двоичные файлы. Поэтому они распределяются между Task.async_stream
"рабочими" и процессом, выполняющим функцию. Поэтому теоретически я мог бы поставить persist
вместе с scrape
внутри Task.async_stream
. Я предпочитаю не делать этого и поддерживать вызовы 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
И приведенная загрузка памяти:
Я не совсем понимаю, как другие предложения в книге соответствуют этой проблеме.
Что бы вы порекомендовали в этом случае? Храните хакерское решение или есть лучшие варианты.