Skip to content

Commit e3973bd

Browse files
committed
fix: correctly cleanup metrics tables
1 parent 6bbd698 commit e3973bd

File tree

3 files changed

+96
-28
lines changed

3 files changed

+96
-28
lines changed

lib/supavisor/metrics_cleaner.ex

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ defmodule Supavisor.MetricsCleaner do
55
require Logger
66

77
@interval :timer.minutes(30)
8+
@name __MODULE__
89

910
def start_link(args),
10-
do: GenServer.start_link(__MODULE__, args, name: __MODULE__)
11+
do: GenServer.start_link(__MODULE__, args, name: @name)
12+
13+
def clean do
14+
GenServer.cast(@name, :clean)
15+
end
1116

1217
def init(_args) do
1318
Logger.info("Starting MetricsCleaner")
@@ -30,7 +35,7 @@ defmodule Supavisor.MetricsCleaner do
3035
do: Logger.warning("Metrics check took: #{exec_time} ms")
3136
end
3237

33-
def handle_info(:check, state) do
38+
def handle_continue(:clean, state) do
3439
Process.cancel_timer(state.check_ref)
3540

3641
:telemetry.span([:supavisor, :metrics_cleaner], %{}, fn ->
@@ -41,39 +46,54 @@ defmodule Supavisor.MetricsCleaner do
4146
{:noreply, %{state | check_ref: check()}}
4247
end
4348

49+
def handle_cast(:clean, state) do
50+
{:noreply, state, {:continue, :clean}}
51+
end
52+
53+
def handle_info(:check, state) do
54+
{:noreply, state, {:continue, :clean}}
55+
end
56+
4457
def handle_info(msg, state) do
4558
Logger.error("Unexpected message: #{inspect(msg)}")
4659
{:noreply, state}
4760
end
4861

49-
def check, do: Process.send_after(self(), :check, @interval)
50-
51-
def loop_and_cleanup_metrics_table do
52-
metrics_table = Supavisor.Monitoring.PromEx.Metrics
53-
tenant_registry_table = :syn_registry_by_name_tenants
54-
55-
func = fn elem, acc ->
56-
with {{_,
57-
%{
58-
type: type,
59-
mode: mode,
60-
user: user,
61-
tenant: tenant,
62-
db_name: db,
63-
search_path: search_path
64-
}} = key, _} <- elem,
65-
[] <- :ets.lookup(tenant_registry_table, {{type, tenant}, user, mode, db, search_path}) do
66-
Logger.warning("Found orphaned metric: #{inspect(key)}")
67-
:ets.delete(metrics_table, key)
68-
69-
acc + 1
70-
else
71-
_ -> acc
72-
end
73-
end
62+
defp check, do: Process.send_after(self(), :check, @interval)
7463

64+
defp loop_and_cleanup_metrics_table do
7565
{_, tids} = Peep.Persistent.storage(Supavisor.Monitoring.PromEx.Metrics)
7666

77-
Enum.each(List.wrap(tids), &:ets.foldl(func, 0, &1))
67+
tids
68+
|> List.wrap()
69+
|> Enum.sum_by(&clean_table/1)
70+
end
71+
72+
@tenant_registry_table :syn_registry_by_name_tenants
73+
74+
defp clean_table(tid) do
75+
func =
76+
fn elem, acc ->
77+
with {{_,
78+
%{
79+
type: type,
80+
mode: mode,
81+
user: user,
82+
tenant: tenant,
83+
db_name: db,
84+
search_path: search_path
85+
}} = key, _} <- elem,
86+
[] <-
87+
:ets.lookup(@tenant_registry_table, {{type, tenant}, user, mode, db, search_path}) do
88+
Logger.warning("Found orphaned metric: #{inspect(key)}")
89+
:ets.delete(tid, key)
90+
91+
acc + 1
92+
else
93+
_ -> acc
94+
end
95+
end
96+
97+
:ets.foldl(func, 0, tid)
7898
end
7999
end

lib/supavisor/monitoring/tenant.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ defmodule Supavisor.PromEx.Plugins.Tenant do
2222
@impl true
2323
def event_metrics(_opts) do
2424
[
25+
system_metrics(),
2526
client_metrics(),
2627
db_metrics()
2728
]
@@ -33,6 +34,19 @@ defmodule Supavisor.PromEx.Plugins.Tenant do
3334
buckets: [1, 5, 10, 100, 1_000, 5_000, 10_000]
3435
end
3536

37+
defp system_metrics do
38+
Event.build(
39+
:supavisor_metrics_cleaner_metrics,
40+
[
41+
counter(
42+
[:supavisor, :metrics_cleaner, :orphaned_metrics],
43+
event_name: [:supavisor, :metrics, :orphaned],
44+
description: "Amount of orphaned metrics that were cleaned up"
45+
)
46+
]
47+
)
48+
end
49+
3650
defp client_metrics do
3751
Event.build(
3852
:supavisor_tenant_client_event_metrics,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
defmodule Supavisor.MetricsCleanerTest do
2+
use ExUnit.Case, async: false
3+
4+
alias Supavisor.PromEx.Plugins.Tenant, as: Metrics
5+
6+
@subject Supavisor.MetricsCleaner
7+
8+
doctest @subject
9+
10+
setup ctx do
11+
:telemetry.attach(ctx, [:supavisor, :metrics_cleaner, :stop], &__MODULE__.handler/4, %{parent: self()})
12+
13+
:ok
14+
end
15+
16+
def handler(_, measurements, _, %{parent: pid}) do
17+
send(pid, {:metrics, measurements})
18+
end
19+
20+
test "metrics for unknown tenant are removed" do
21+
:ok = Metrics.emit_telemetry_for_tenant({{{:single, "non-existent"}, "foo", :transaction, "bar", nil}, 2137})
22+
metrics = Supavisor.Monitoring.PromEx.get_metrics()
23+
24+
assert IO.iodata_to_binary(metrics) =~ ~r/non-existent/
25+
26+
@subject.clean()
27+
28+
assert_receive {:metrics, _}
29+
30+
metrics = Supavisor.Monitoring.PromEx.get_metrics()
31+
32+
refute IO.iodata_to_binary(metrics) =~ ~r/non-existent/
33+
end
34+
end

0 commit comments

Comments
 (0)