First
[anni] / lib / pleroma / healthcheck.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.Healthcheck do
6   @moduledoc """
7   Module collects metrics about app and assign healthy status.
8   """
9   alias Pleroma.Healthcheck
10   alias Pleroma.Repo
11
12   @derive Jason.Encoder
13   defstruct pool_size: 0,
14             active: 0,
15             idle: 0,
16             memory_used: 0,
17             job_queue_stats: nil,
18             healthy: true
19
20   @type t :: %__MODULE__{
21           pool_size: non_neg_integer(),
22           active: non_neg_integer(),
23           idle: non_neg_integer(),
24           memory_used: number(),
25           job_queue_stats: map(),
26           healthy: boolean()
27         }
28
29   @spec system_info() :: t()
30   def system_info do
31     %Healthcheck{
32       memory_used: Float.round(:recon_alloc.memory(:allocated) / 1024 / 1024, 2)
33     }
34     |> assign_db_info()
35     |> assign_job_queue_stats()
36     |> check_health()
37   end
38
39   defp assign_db_info(healthcheck) do
40     database = Pleroma.Config.get([Repo, :database])
41
42     query =
43       "select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;"
44
45     result = Repo.query!(query)
46     pool_size = Pleroma.Config.get([Repo, :pool_size])
47
48     db_info =
49       Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->
50         if state == "active" do
51           Map.put(states, :active, states.active + cnt)
52         else
53           Map.put(states, :idle, states.idle + cnt)
54         end
55       end)
56       |> Map.put(:pool_size, pool_size)
57
58     Map.merge(healthcheck, db_info)
59   end
60
61   defp assign_job_queue_stats(healthcheck) do
62     stats = Pleroma.JobQueueMonitor.stats()
63     Map.put(healthcheck, :job_queue_stats, stats)
64   end
65
66   @spec check_health(Healthcheck.t()) :: Healthcheck.t()
67   def check_health(%{pool_size: pool_size, active: active} = check)
68       when active >= pool_size do
69     %{check | healthy: false}
70   end
71
72   def check_health(check), do: check
73 end