File size: 2,764 Bytes
2e92879
 
 
 
 
f085bb2
2e92879
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2ff4b5
c2de526
2e92879
 
abcde98
 
 
 
 
 
 
 
28b00d6
 
 
 
c2de526
2e92879
 
 
c2de526
2e92879
 
abcde98
 
 
 
 
 
 
2e92879
 
 
 
abcde98
 
 
 
 
 
28b00d6
 
 
 
 
2e92879
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88a54bd
2e92879
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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