First
[anni] / lib / pleroma / gun / connection_pool / reclaimer.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
6   use GenServer, restart: :temporary
7
8   defp registry, do: Pleroma.Gun.ConnectionPool
9
10   def start_monitor do
11     pid =
12       case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
13         {:ok, pid} ->
14           pid
15
16         {:error, {:already_registered, pid}} ->
17           pid
18       end
19
20     {pid, Process.monitor(pid)}
21   end
22
23   @impl true
24   def init(_) do
25     {:ok, nil, {:continue, :reclaim}}
26   end
27
28   @impl true
29   def handle_continue(:reclaim, _) do
30     max_connections = Pleroma.Config.get([:connections_pool, :max_connections])
31
32     reclaim_max =
33       [:connections_pool, :reclaim_multiplier]
34       |> Pleroma.Config.get()
35       |> Kernel.*(max_connections)
36       |> round
37       |> max(1)
38
39     :telemetry.execute([:pleroma, :connection_pool, :reclaim, :start], %{}, %{
40       max_connections: max_connections,
41       reclaim_max: reclaim_max
42     })
43
44     # :ets.fun2ms(
45     # fn {_, {worker_pid, {_, used_by, crf, last_reference}}} when used_by == [] ->
46     #   {worker_pid, crf, last_reference} end)
47     unused_conns =
48       Registry.select(
49         registry(),
50         [
51           {{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
52         ]
53       )
54
55     case unused_conns do
56       [] ->
57         :telemetry.execute(
58           [:pleroma, :connection_pool, :reclaim, :stop],
59           %{reclaimed_count: 0},
60           %{
61             max_connections: max_connections
62           }
63         )
64
65         {:stop, :no_unused_conns, nil}
66
67       unused_conns ->
68         reclaimed =
69           unused_conns
70           |> Enum.sort(fn {_pid1, crf1, last_reference1}, {_pid2, crf2, last_reference2} ->
71             crf1 <= crf2 and last_reference1 <= last_reference2
72           end)
73           |> Enum.take(reclaim_max)
74
75         reclaimed
76         |> Enum.each(fn {pid, _, _} ->
77           DynamicSupervisor.terminate_child(Pleroma.Gun.ConnectionPool.WorkerSupervisor, pid)
78         end)
79
80         :telemetry.execute(
81           [:pleroma, :connection_pool, :reclaim, :stop],
82           %{reclaimed_count: Enum.count(reclaimed)},
83           %{max_connections: max_connections}
84         )
85
86         {:stop, :normal, nil}
87     end
88   end
89 end