kiola / lib /srh /redis /client_registry.ex
Scott Hiett
Improve handling for multi exec commands when unable to connect to the Redis server
1b2415c
defmodule Srh.Redis.ClientRegistry do
use GenServer
def start_link() do
GenServer.start_link(__MODULE__, %{}, [])
end
def init(_opts) do
{
:ok,
%{
worker_pids: [],
last_worker_index: 0,
currently_borrowed_pids: []
}
}
end
def find_worker(registry) do
GenServer.call(registry, {:find_worker})
end
def borrow_worker(registry) do
GenServer.call(registry, {:borrow_worker})
end
def return_worker(registry, pid) do
GenServer.cast(registry, {:return_worker, pid})
end
def add_worker(registry, pid) do
GenServer.cast(registry, {:add_worker, pid})
end
def destroy_workers(registry) do
GenServer.cast(registry, {:destroy_workers})
end
def handle_call({:borrow_worker}, _from, state) do
case do_find_worker(state) do
{{:error, msg}, state_update} ->
{:reply, {:error, msg}, state_update}
{{:ok, pid}, state_update} ->
# We want to put this pid into the borrowed pids state list
{
:reply,
{:ok, pid},
%{
state_update
| currently_borrowed_pids:
[pid | state_update.currently_borrowed_pids]
|> Enum.uniq()
}
}
end
end
def handle_call({:find_worker}, _from, state) do
{res, state_update} = do_find_worker(state)
{:reply, res, state_update}
end
def handle_call(_msg, _from, state) do
{:reply, :ok, state}
end
def handle_cast({:add_worker, pid}, state) do
Process.monitor(pid)
{
:noreply,
%{
state
| worker_pids:
[pid | state.worker_pids]
|> Enum.uniq()
}
}
end
def handle_cast({:destroy_workers}, state) do
for worker_pid <- state.worker_pids do
Srh.Redis.ClientWorker.destroy_redis(worker_pid)
end
{:noreply, %{state | worker_pids: [], last_worker_index: 0}}
end
def handle_cast({:return_worker, pid}, state) do
# Remove it from the borrowed array
{
:noreply,
%{state | currently_borrowed_pids: List.delete(state.currently_borrowed_pids, pid)}
}
end
def handle_cast(_msg, state) do
{:noreply, state}
end
def handle_info({:DOWN, pid, :normal, _ref}, state) do
{:noreply, %{state | worker_pids: List.delete(state.worker_pids, pid)}}
end
def handle_info(_msg, state) do
{:noreply, state}
end
defp do_find_worker(state) do
filtered_pids =
state.worker_pids
|> Enum.filter(&(!Enum.member?(state.currently_borrowed_pids, &1)))
case length(filtered_pids) do
0 ->
{{:error, :none_available}, state}
len ->
target = state.last_worker_index + 1
corrected_target =
case target >= len do
true -> 0
false -> target
end
{
{:ok, Enum.at(state.worker_pids, corrected_target)},
%{state | last_worker_index: corrected_target}
}
end
end
end