summaryrefslogtreecommitdiff
path: root/lib/pleroma/gun/connection_pool/reclaimer.ex
blob: c37b62bf2f21b7b812037045c90644a76124b030 (plain)
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
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
  use GenServer, restart: :temporary

  @registry Pleroma.Gun.ConnectionPool

  def start_monitor do
    pid =
      case :gen_server.start(__MODULE__, [], name: {:via, Registry, {@registry, "reclaimer"}}) do
        {:ok, pid} ->
          pid

        {:error, {:already_registered, pid}} ->
          pid
      end

    {pid, Process.monitor(pid)}
  end

  @impl true
  def init(_) do
    {:ok, nil, {:continue, :reclaim}}
  end

  @impl true
  def handle_continue(:reclaim, _) do
    max_connections = Pleroma.Config.get([:connections_pool, :max_connections])

    reclaim_max =
      [:connections_pool, :reclaim_multiplier]
      |> Pleroma.Config.get()
      |> Kernel.*(max_connections)
      |> round
      |> max(1)

    :telemetry.execute([:pleroma, :connection_pool, :reclaim, :start], %{}, %{
      max_connections: max_connections,
      reclaim_max: reclaim_max
    })

    # :ets.fun2ms(
    # fn {_, {worker_pid, {_, used_by, crf, last_reference}}} when used_by == [] ->
    #   {worker_pid, crf, last_reference} end)
    unused_conns =
      Registry.select(
        @registry,
        [
          {{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
        ]
      )

    case unused_conns do
      [] ->
        :telemetry.execute(
          [:pleroma, :connection_pool, :reclaim, :stop],
          %{reclaimed_count: 0},
          %{
            max_connections: max_connections
          }
        )

        {:stop, :no_unused_conns, nil}

      unused_conns ->
        reclaimed =
          unused_conns
          |> Enum.sort(fn {_pid1, crf1, last_reference1}, {_pid2, crf2, last_reference2} ->
            crf1 <= crf2 and last_reference1 <= last_reference2
          end)
          |> Enum.take(reclaim_max)

        reclaimed
        |> Enum.each(fn {pid, _, _} ->
          DynamicSupervisor.terminate_child(Pleroma.Gun.ConnectionPool.WorkerSupervisor, pid)
        end)

        :telemetry.execute(
          [:pleroma, :connection_pool, :reclaim, :stop],
          %{reclaimed_count: Enum.count(reclaimed)},
          %{max_connections: max_connections}
        )

        {:stop, :normal, nil}
    end
  end
end