defmodule Srh.Redis.Client do use GenServer alias Srh.Redis.ClientRegistry alias Srh.Redis.ClientWorker @idle_death_time 1000 * 60 * 15 # 15 minutes def start_link(max_connections, connection_info) do GenServer.start_link(__MODULE__, {max_connections, connection_info}, []) end def init({max_connections, connection_info}) do Process.send(self(), :create_registry, []) { :ok, %{ registry_pid: nil, idle_death_ref: nil, max_connections: max_connections, connection_info: connection_info } } end def find_worker(client) do GenServer.call(client, {:find_worker}) end def borrow_worker(client) do GenServer.call(client, {:borrow_worker}) end def return_worker(client, pid) do GenServer.cast(client, {:return_worker, pid}) end def destroy_workers(client) do GenServer.cast(client, {:destroy_workers}) end def handle_call({:find_worker}, _from, %{registry_pid: registry_pid} = state) when is_pid(registry_pid) do {:ok, worker} = ClientRegistry.find_worker(registry_pid) Process.send(self(), :reset_idle_death, []) {:reply, worker, state} end def handle_call({:borrow_worker}, _from, %{registry_pid: registry_pid} = state) when is_pid(registry_pid) do {:ok, worker} = ClientRegistry.borrow_worker(registry_pid) Process.send(self(), :reset_idle_death, []) {:reply, worker, state} end def handle_call(_msg, _from, state) do {:reply, :ok, state} end def handle_cast({:return_worker, pid}, %{registry_pid: registry_pid} = state) when is_pid(pid) and is_pid(registry_pid) do ClientRegistry.return_worker(registry_pid, pid) {:noreply, state} end def handle_cast({:destroy_workers}, state) do ClientRegistry.destroy_workers(state.registry_pid) {:stop, :normal, state} end def handle_cast(_msg, state) do {:noreply, state} end def handle_info(:idle_death, state) do ClientRegistry.destroy_workers(state.registry_pid) {:stop, :normal, state} end def handle_info(:reset_idle_death, state) do if state.idle_death_ref != nil do Process.cancel_timer(state.idle_death_ref) end { :noreply, %{state | idle_death_ref: Process.send_after(self(), :idle_death, @idle_death_time)} } end def handle_info(:create_registry, state) do {:ok, pid} = ClientRegistry.start_link() # Spin up three workers for _ <- 1..Map.get(state.connection_info, "max_connections", 3) do {:ok, worker} = ClientWorker.start_link(state.connection_info) ClientRegistry.add_worker(pid, worker) end {:noreply, %{state | registry_pid: pid}} end def handle_info(_msg, state) do {:noreply, state} end end