• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / supavisor / 6af7f2db69bcd25b3c0152c1ae07f7f165c55681-PR-573

24 Jan 2025 12:13PM UTC coverage: 46.518% (-0.8%) from 47.304%
6af7f2db69bcd25b3c0152c1ae07f7f165c55681-PR-573

Pull #573

github

hauleth
chore: ignore tests support files in coverage reports
Pull Request #573: chore: ignore tests support files in coverage reports

962 of 2068 relevant lines covered (46.52%)

206.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.39
/lib/supavisor/monitoring/prom_ex.ex
1
defmodule Supavisor.Monitoring.PromEx do
2
  @moduledoc """
3
  This module configures the PromEx application for Supavisor. It defines
4
  the plugins used for collecting metrics, including built-in plugins and custom ones,
5
  and provides a function to remove remote metrics associated with a specific tenant.
6
  """
7

8
  use PromEx, otp_app: :supavisor
9
  require Logger
10

11
  alias PromEx.Plugins
12
  alias Supavisor, as: S
13
  alias Supavisor.PromEx.Plugins.{OsMon, Tenant}
14

15
  @impl true
16
  def plugins do
17
    poll_rate = Application.fetch_env!(:supavisor, :prom_poll_rate)
×
18

19
    [
20
      # PromEx built in plugins
21
      Plugins.Application,
22
      Plugins.Beam,
23
      {Plugins.Phoenix, router: SupavisorWeb.Router, endpoint: SupavisorWeb.Endpoint},
24
      Plugins.Ecto,
25

26
      # Custom PromEx metrics plugins
27
      {OsMon, poll_rate: poll_rate},
28
      {Tenant, poll_rate: poll_rate}
29
    ]
30
  end
31

32
  @spec remove_metrics(S.id()) :: :ok
33
  def remove_metrics({{type, tenant}, user, mode, db_name, search_path} = id) do
34
    Logger.debug("Removing metrics for #{inspect(id)}")
15✔
35

36
    meta = %{
15✔
37
      tenant: tenant,
38
      user: user,
39
      mode: mode,
40
      type: type,
41
      db_name: db_name,
42
      search_path: search_path
43
    }
44

45
    {_, tids} = Peep.Persistent.storage(Supavisor.Monitoring.PromEx.Metrics)
15✔
46

47
    tids
48
    |> List.wrap()
49
    |> Enum.each(
15✔
50
      &:ets.select_delete(&1, [
15✔
51
        {{{:_, meta}, :_}, [], [true]},
52
        {{{:_, meta, :_}, :_}, [], [true]}
53
      ])
54
    )
55
  end
56

57
  @spec set_metrics_tags() :: map()
58
  def set_metrics_tags do
59
    [_, host] = node() |> Atom.to_string() |> String.split("@")
×
60

61
    metrics_tags = %{
×
62
      region: Application.fetch_env!(:supavisor, :region),
63
      host: host
64
    }
65

66
    metrics_tags =
×
67
      case short_node_id() do
68
        nil -> metrics_tags
×
69
        short_alloc_id -> Map.put(metrics_tags, :short_alloc_id, short_alloc_id)
×
70
      end
71

72
    Application.put_env(:supavisor, :metrics_tags, metrics_tags)
×
73
    metrics_tags
×
74
  end
75

76
  @spec short_node_id() :: String.t() | nil
77
  def short_node_id do
78
    with {:ok, fly_alloc_id} when is_binary(fly_alloc_id) <-
×
79
           Application.fetch_env(:supavisor, :fly_alloc_id),
80
         [short_alloc_id, _] <- String.split(fly_alloc_id, "-", parts: 2) do
×
81
      short_alloc_id
×
82
    else
83
      _ -> nil
84
    end
85
  end
86

87
  @spec get_metrics() :: String.t()
88
  def get_metrics do
89
    metrics_tags =
8✔
90
      case Application.fetch_env(:supavisor, :metrics_tags) do
91
        :error -> set_metrics_tags()
×
92
        {:ok, tags} -> tags
8✔
93
      end
94

95
    def_tags = Enum.map_join(metrics_tags, ",", fn {k, v} -> "#{k}=\"#{v}\"" end)
8✔
96

97
    metrics =
8✔
98
      PromEx.get_metrics(__MODULE__)
99
      |> String.split("\n")
100
      |> Enum.map_join("\n", &parse_and_add_tags(&1, def_tags))
7,911✔
101

102
    Supavisor.Monitoring.PromEx.ETSCronFlusher
103
    |> PromEx.ETSCronFlusher.defer_ets_flush()
8✔
104

105
    metrics
8✔
106
  end
107

108
  @spec do_cache_tenants_metrics() :: list
109
  def do_cache_tenants_metrics do
110
    metrics = get_metrics() |> String.split("\n")
5✔
111

112
    pools =
5✔
113
      Registry.select(Supavisor.Registry.TenantClients, [{{:"$1", :_, :_}, [], [:"$1"]}])
114
      |> Enum.uniq()
115

116
    _ =
5✔
117
      Enum.reduce(pools, metrics, fn {{_type, tenant}, _, _, _, _}, acc ->
118
        {matched, rest} = Enum.split_with(acc, &String.contains?(&1, "tenant=\"#{tenant}\""))
3✔
119

120
        if matched != [] do
3✔
121
          Cachex.put(Supavisor.Cache, {:metrics, tenant}, Enum.join(matched, "\n"))
3✔
122
        end
123

124
        rest
3✔
125
      end)
126

127
    pools
5✔
128
  end
129

130
  @spec get_tenant_metrics(String.t()) :: String.t()
131
  def get_tenant_metrics(tenant) do
132
    case Cachex.get(Supavisor.Cache, {:metrics, tenant}) do
×
133
      {_, metrics} when is_binary(metrics) -> metrics
×
134
      _ -> ""
×
135
    end
136
  end
137

138
  @spec parse_and_add_tags(String.t(), String.t()) :: String.t()
139
  defp parse_and_add_tags(line, def_tags) do
140
    case Regex.run(~r/(?!\#)^(\w+)(?:{(.*?)})?\s*(.+)$/, line) do
7,911✔
141
      nil ->
142
        line
1,040✔
143

144
      [_, key, tags, value] ->
145
        tags = clean_string(tags)
6,871✔
146

147
        tags =
6,871✔
148
          if tags == "" do
149
            def_tags
344✔
150
          else
151
            "#{tags},#{def_tags}"
6,527✔
152
          end
153

154
        "#{key}{#{tags}} #{value}"
6,871✔
155
    end
156
  end
157

158
  @spec clean_string(String.t()) :: String.t()
159
  def clean_string(metric_string) do
160
    regex = ~r/=\s*"([^"]*?)"/
6,872✔
161

162
    String.replace(metric_string, regex, fn match ->
6,872✔
163
      [_, value] = Regex.run(regex, match)
26,756✔
164

165
      cleaned =
26,756✔
166
        value
167
        |> String.replace(~r/\n+/, "")
168
        |> String.trim()
169

170
      if value != cleaned do
26,756✔
171
        Logger.warning("Tag validation: #{inspect(value)} / #{inspect(cleaned)}")
5✔
172
      end
173

174
      "=\"#{cleaned}\""
26,756✔
175
    end)
176
  end
177
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc