# Pleroma: A lightweight social networking server # Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.Reclaimer do use GenServer, restart: :temporary defp registry, do: 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