floof/lib/floof/packet_spool.ex

84 lines
1.8 KiB
Elixir
Raw Normal View History

2022-11-26 13:30:53 +00:00
defmodule Floof.PacketSpool do
@moduledoc """
disk-backed packet spooling management
saves packets to disk, and manages encoding/decoding
the user is encouraged to use a backing disk
with long commit times to improve performance and avoid disk churn
"""
use GenServer
require Logger
def start_link(initval \\ "/var/spool/floof") do
GenServer.start_link(__MODULE__, initval, name: __MODULE__)
end
### client interface
## packet is expected to be of type :XferBlob
def store(hash, packet) do
GenServer.call(__MODULE__, {:store, hash, packet})
end
def fetch(hash) do
GenServer.call(__MODULE__, {:fetch, hash})
end
def drop(hash) do
GenServer.cast(__MODULE__, {:drop, hash})
end
### server interface
@impl true
def init(state) do
{:ok, state}
end
@impl true
def handle_call({:store, hash, packet}, _, state) do
echain(
state,
fn -> :FloofProtocol.encode(:XferBlob, packet) end,
&File.write(hash_to_path(hash, state), &1)
)
end
@impl true
def handle_call({:fetch, hash}, _, state) do
echain(
state,
fn -> File.read(hash_to_path(hash, state)) end,
&:FloofProtocol.decode(:XferBlob, &1)
)
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)}")
end
{:noreply, state}
end
# internal interface
defp hash_to_path(hash, state) do
state <> "/" <> Base.url_encode64(hash)
end
defp echain(state, handler1, handler2) do
retval = try do
case handler1.() do
{:ok, encd} -> handler2.(encd)
x -> x
end
catch
y, z -> {:error, y, z}
end
{:reply, retval, state}
end
end