prevent dead-lock during garbage-collection

This commit is contained in:
Alain Zscheile 2022-11-27 23:12:03 +01:00
parent 771854002f
commit 83327c71dd
2 changed files with 49 additions and 58 deletions

View file

@ -46,10 +46,6 @@ defmodule Floof.PacketSpool do
@impl true
def init(state) do
:ok = File.mkdir_p(state)
# in 5 seconds
Process.send_after(self(), :collect_garbage, 5 * 1000)
{:ok, state}
end
@ -134,59 +130,50 @@ defmodule Floof.PacketSpool do
end
@impl true
def handle_cast({:drop, hash}, state) do
case File.rm(hash_to_path(hash, state)) do
:ok -> nil
{:error, e} -> Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}")
def handle_call({:collect_garbage_keep_only, to_keep}, _, state) do
{:ok, items} = File.ls(state)
present =
MapSet.new(
Enum.flat_map(items, fn x ->
case Base.url_decode64(x) do
{:ok, y} ->
[y]
{:error, e} ->
Logger.warn("spool: unable to decode entry name #{x}: #{inspect(e)}")
[]
end
end)
)
to_keep = MapSet.new(to_keep)
to_remove = MapSet.difference(present, to_keep)
if not Enum.empty?(to_remove) do
Logger.debug("spool: to_remove #{inspect(to_remove)}")
for hash <- to_remove do
case File.rm(hash_to_path(hash, state)) do
:ok ->
nil
{:error, e} ->
Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}")
end
end
end
{:noreply, state}
end
@impl true
def handle_info(:collect_garbage, state) do
case GenServer.call(Floof.SessionManager, :get_keep_only, :infinity) do
:error ->
nil
{:ok, to_keep} ->
{:ok, items} = File.ls(state)
present =
MapSet.new(
Enum.flat_map(items, fn x ->
case Base.url_decode64(x) do
{:ok, y} ->
[y]
{:error, e} ->
Logger.warn("spool: unable to decode entry name #{x}: #{inspect(e)}")
[]
end
end)
)
to_keep = MapSet.new(to_keep)
to_remove = MapSet.difference(present, to_keep)
if not Enum.empty?(to_remove) do
Logger.debug("spool: to_remove #{inspect(to_remove)}")
for hash <- to_remove do
case File.rm(hash_to_path(hash, state)) do
:ok ->
nil
{:error, e} ->
Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}")
end
end
end
def handle_cast({:drop, hash}, state) do
case File.rm(hash_to_path(hash, state)) do
:ok -> nil
{:error, e} -> Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}")
end
# do this again in 15 seconds
Process.send_after(self(), :collect_garbage, 15 * 1000)
{:noreply, state}
end

View file

@ -15,6 +15,7 @@ defmodule Floof.SessionManager do
@impl true
def init({file, opts}) do
{:ok, file} = :dets.open_file(file, opts ++ [type: :bag])
Process.send_after(self(), :collect_garbage, 5 * 1000)
{:ok, %Floof.SessionManager{dets_file: file}}
end
@ -143,16 +144,19 @@ defmodule Floof.SessionManager do
end
@impl true
def handle_call(:get_keep_only, _, state) do
{:reply,
case :dets.match(state.dets_file, {'_', '$2'}) do
{:error, e} ->
Logger.error("garbage collection error: #{inspect(e)}")
:error
def handle_info(:collect_garbage, state) do
case :dets.match(state.dets_file, {'_', '$2'}) do
{:error, e} ->
Logger.error("garbage collection error: #{inspect(e)}")
Process.send_after(self(), :collect_garbage, 60 * 1000)
keys ->
{:ok, Enum.map(keys, fn [x] -> x end)}
end, state}
keys ->
to_keep = Enum.map(keys, fn [x] -> x end)
GenServer.call(Floof.PacketSpool, {:collect_garbage_keep_only, to_keep}, :infinity)
Process.send_after(self(), :collect_garbage, 15 * 1000)
end
{:noreply, state}
end
defp all_session_keys(state) do