Hey all,
I’m wondering how to reduce some of the RAM usage (and maybe some tips for diagnosing which processes and function calls are actually hogging RAM) in my LiveView application. My use case is that I have a set of geographical features stored in Postgres with the great geo_postgis library. These features are broken up into sectors, each of which are defined by a rectangular polygon. When the user moves the map on the webpage, I have a hook send an event to the LiveView process with the bounding box of the map area in view. Then, the event handler calculates which sectors are in view and gets the features by their associated sector ID from the in-memory Cachex cache (this obviously is a large portion of RAM usage, but the total number of features is around 20,000 and is around 17MB of CSV file total). The event handler assigns prepends the sector IDs to the :loaded_sector_ids list assign in the socket (so it doesn’t send them multiple times), and sends the data to the client with push_event/3
using Enum.reduce with the socket. The code is below:
def handle_event("load-data", %{"bounds" => bounds}, socket) do
socket =
bounds
|> MyApp.get_intersecting_features() # loads data in [{sector_id, sector_data}, ...] format
|> Enum.reject(fn {sector_id, _sector_data} -> sector_id in socket.assigns.loaded_sector_ids end)
|> Enum.reduce(socket, fn {sector_id, sector_data}, s ->
s
|> push_event("data", %{sector_id: sector_id, data: sector_data})
|> update(:loaded_sector_ids, fn sector_ids -> [sector_id | sector_ids] end)
end)
{:noreply, socket}
end
My guess is that the heavy memory usage is caused by the large immutable lists of features are being copied to the socket in the Enum.reduce call, but I’m not sure how to structure this in a better way to reduce that. Would it make sense to run this in more of a recursion manner? On receiving the bounds, I could load the list of sector_ids that need to be sent to the client, then basically send a message to the current process with the remaining list of sector_ids to load and send to the client until that list is empty?
Looking forward to hearing suggestions, and thanks in advance!
Gus