File size: 3,000 Bytes
2e92879
 
 
 
 
 
 
 
 
 
 
 
abcde98
 
2e92879
 
 
 
 
 
 
 
abcde98
 
 
 
 
 
 
 
2e92879
 
 
 
 
 
 
 
abcde98
 
 
 
 
 
 
 
 
 
 
 
1b2415c
 
 
abcde98
 
2e92879
 
 
abcde98
 
 
 
 
2e92879
 
 
 
 
 
 
 
 
 
 
1b2415c
 
 
2e92879
 
 
 
 
 
249b683
2e92879
 
 
 
 
abcde98
 
249b683
 
 
 
abcde98
 
2e92879
 
 
 
 
 
 
 
 
 
 
abcde98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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