Spaces:
Sleeping
Sleeping
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 | |