84 lines
1.8 KiB
Elixir
84 lines
1.8 KiB
Elixir
|
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
|