From 3c6211dd0818100dceb02209e33eb2520669ea92 Mon Sep 17 00:00:00 2001 From: Aleksey Sabilin Date: Mon, 20 Jul 2026 01:03:53 +0300 Subject: [PATCH] fix: avoid empty node metrics race in CT (collect on start + poll). Refs EventHub/EventHubBack#47 --- src/infra/node_monitor.erl | 4 +-- test/api/admins/admin_stats_tests.erl | 37 +++++++++++++++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/infra/node_monitor.erl b/src/infra/node_monitor.erl index e50cc4f..2a9d845 100644 --- a/src/infra/node_monitor.erl +++ b/src/infra/node_monitor.erl @@ -41,8 +41,8 @@ init([]) -> mnesia:wait_for_tables([node_metric], 5000), % Создаём ETS-таблицу для кеширования предыдущих значений io ets:new(?IO_CACHE_TAB, [named_table, public, set, {keypos, 1}]), - % Запускаем периодический сбор метрик и очистку - schedule_collect(), + % Первый сбор сразу (иначе окно ~5с без метрик), далее по интервалу. + self() ! collect, schedule_cleanup(), {ok, #{}}. diff --git a/test/api/admins/admin_stats_tests.erl b/test/api/admins/admin_stats_tests.erl index f3194c2..57725f3 100644 --- a/test/api/admins/admin_stats_tests.erl +++ b/test/api/admins/admin_stats_tests.erl @@ -227,6 +227,25 @@ test_calendar_stats(Token) -> test_node_metrics_history(Token) -> ct:pal(" TEST: Node metrics history"), + %% node_monitor пишет раз в 5с; в быстром CI stats-кейс может успеть раньше. + case whereis(node_monitor) of + undefined -> ok; + Pid when is_pid(Pid) -> Pid ! collect + end, + Metrics = wait_node_metrics(Token, 20, 500), + ?assert(is_list(Metrics)), + ?assert(length(Metrics) >= 1), + First = hd(Metrics), + ?assert(maps:is_key(<<"timestamp">>, First)), + ?assert(maps:is_key(<<"node">>, First)), + ?assert(maps:is_key(<<"memory_total">>, First)), + ?assert(maps:is_key(<<"memory_available">>, First)), + ?assert(maps:is_key(<<"cpu_utilization">>, First)), + ct:pal(" OK: ~p metrics received", [length(Metrics)]). + +wait_node_metrics(_Token, 0, _SleepMs) -> + []; +wait_node_metrics(Token, Attempts, SleepMs) -> Now = calendar:universal_time(), FromSec = calendar:datetime_to_gregorian_seconds(Now) - 300, ToSec = calendar:datetime_to_gregorian_seconds(Now) + 300, @@ -237,17 +256,13 @@ test_node_metrics_history(Token) -> Path = <<"/v1/admin/nodes/metrics?from=", From/binary, "&to=", To/binary>>, {ok, 200, _, Body} = api_test_runner:admin_request(get, Path, Token, <<"">>), Metrics = jsx:decode(list_to_binary(Body), [return_maps]), - ?assert(is_list(Metrics)), - % Так как монитор работает, метрики уже должны быть - ?assert(length(Metrics) >= 1), - % Проверяем структуру первой метрики - First = hd(Metrics), - ?assert(maps:is_key(<<"timestamp">>, First)), - ?assert(maps:is_key(<<"node">>, First)), - ?assert(maps:is_key(<<"memory_total">>, First)), - ?assert(maps:is_key(<<"memory_available">>, First)), - ?assert(maps:is_key(<<"cpu_utilization">>, First)), - ct:pal(" OK: ~p metrics received", [length(Metrics)]). + case is_list(Metrics) andalso length(Metrics) >= 1 of + true -> + Metrics; + false -> + timer:sleep(SleepMs), + wait_node_metrics(Token, Attempts - 1, SleepMs) + end. test_avg_ticket_resolution_after_close(Token, UserToken) -> ct:pal(" TEST: avg_ticket_resolution_h after closing ticket"),