Hello everyone.
I’m building a webapp with LiveView and ran into the following situation. On mount I load a Tabulator hook without any data. After the tabulator hook is mounted, it sends an event to the socket(“load”), and then I run an async task(load_data) to fetch the results and send them back to the hook via push_event.
The thing is, this query returns a few thousand results, and I thought that once the push_event completed the socket memory would be released, since I’m not storing the data in assigns. But that doesn’t seem to be the case.
I don’t load anything initially. The hook mounts, triggers an event, the event is processed in an async task (so it won’t block the LiveView), and when it finishes it sends the data back via push_event. Then the hook itself takes care of building the table, filters, views, and only comunicate with the LV when necessary to change values in the DB using push from the hook.
What am I doing wrong that the memory get so many spikes and does not reduce?
I have tried to reduce the data using chunks, instead of sending it all at once, but without success.
I know it is possible to use stream in the socket to avoid assigns in the socket, but using push event should not keep the data in the LV, isn’t it?
Here is a summary of the code: (I have put the List.duplicate to test in dev, since in prod I have use cases of 9000 rows, but in dev I simulate it with 300 real rows and increased it using duplicates).
While inspecting the dashboard I have noticed that when the socket mount, the process have 56.8 MB and also the module gets 82.2 MB of memory (the second one disappears after a some time). Couldn’t post the photo here, since I’m a new user.
def mount(_params, _session, socket) do
account_id = socket.assigns.current_scope.user.last_account_id
user_id = socket.assigns.current_scope.user.id
if connected?(socket) do
data_subscribe(account_id)
end
employees = list_employees_for_account(account_id)
form = to_form(Action.create_action_changeset(%Action{}, %{}))
{:ok,
assign(socket,
form: form,
employees: employees,
selected_products: [],
checked: false,
has_job_pending: false,
show_task_modal: false,
is_loading: true,
progress: 0,
options: []
)}
end
def handle_event("load", _params, socket) do
account_id = socket.assigns.current_scope.user.last_account_id
load_data(account_id)
{:noreply, socket}
end
def load_data(account_id) do
Task.async(fn ->
data=
from(i in Item,
where: i.account_id == ^account_id,
preload: [:item_metrics, :sku, actions: :employee]
)
|> Repo.all()
|> Enum.flat_map(fn item ->
List.duplicate(item, 20)
end)
|> Enum.map(&Formaters.format_item/1)
options = unique_performance_options(data)
{:data, {data, options}}
end)
end
def handle_info({:load, _payload}, socket) do
account_id = socket.assigns.current_scope.user.last_account_id
load_data(account_id)
{:noreply, socket}
end
def handle_info({ref, {:data, data}}, socket) do
Process.demonitor(ref, [:flush])
{data, options} = data
{:noreply,
socket
|> assign(is_loading: false)
|> assign(options: options)
|> push_event("load_data", %{data: data})}
end






















