From 31c2a1b7d7b1f248ef515eee9df4338a1d6ee86f Mon Sep 17 00:00:00 2001 From: Aleksey Sabilin Date: Sat, 18 Jul 2026 23:33:56 +0300 Subject: [PATCH] fix: subscribe stats_collector after Mnesia tables are ready Backfill ran during init_tables before wait_for_tables, so dirty reads hit {no_exists,user} and crash-looped eventhub on IFT. --- src/eventhub_app.erl | 2 ++ src/infra/infra_mnesia.erl | 2 +- src/infra/stats_collector.erl | 21 +++++++++++++++++++-- src/infra/stats_tops.erl | 7 ++++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/eventhub_app.erl b/src/eventhub_app.erl index 6a65d3d..051901a 100644 --- a/src/eventhub_app.erl +++ b/src/eventhub_app.erl @@ -52,6 +52,8 @@ start_application() -> ok = infra_mnesia:init_tables(), ok = infra_mnesia:wait_for_tables(), ok = migration_engine:ensure_applied(), + %% После wait + migrations: иначе backfill ловит {no_exists, user} на ещё не загруженных таблицах. + ok = stats_collector:subscribe(), calendar_html_renderer:init_cache(), application:ensure_all_started(cowboy), start_http(), % Пользовательский API (8080) diff --git a/src/infra/infra_mnesia.erl b/src/infra/infra_mnesia.erl index 7d4f968..59b6670 100644 --- a/src/infra/infra_mnesia.erl +++ b/src/infra/infra_mnesia.erl @@ -125,7 +125,7 @@ handle_call(init_tables, _From, State) -> true -> ok end, ok = create_indices(), - ok = stats_collector:subscribe(), + %% stats_collector:subscribe — после wait_for_tables + migrations (eventhub_app) ok = start_cleanup_timer(), {reply, ok, State}; diff --git a/src/infra/stats_collector.erl b/src/infra/stats_collector.erl index 468c3d4..dcaa72c 100644 --- a/src/infra/stats_collector.erl +++ b/src/infra/stats_collector.erl @@ -127,7 +127,18 @@ do_subscribe(State) -> false -> stats_tops:rebuild() end, lists:foreach(fun(Table) -> - mnesia:subscribe({table, Table, detailed}) + case lists:member(Table, mnesia:system_info(tables)) of + true -> + case mnesia:wait_for_tables([Table], 60000) of + ok -> mnesia:subscribe({table, Table, detailed}); + {timeout, _} -> + error_logger:warning_msg( + "stats_collector: skip subscribe ~p (wait timeout)~n", [Table]) + end; + false -> + error_logger:warning_msg( + "stats_collector: skip subscribe ~p (no table)~n", [Table]) + end end, ?SUB_TABLES), State#state{ready = true, subscribed = true}. @@ -371,7 +382,13 @@ backfill_all() -> safe_match(Table, Pattern) -> case lists:member(Table, mnesia:system_info(tables)) of - true -> mnesia:dirty_match_object(Pattern); + true -> + try mnesia:dirty_match_object(Pattern) of + Rows when is_list(Rows) -> Rows + catch + exit:{aborted, {no_exists, _}} -> []; + error:{aborted, {no_exists, _}} -> [] + end; false -> [] end. diff --git a/src/infra/stats_tops.erl b/src/infra/stats_tops.erl index 46925d5..e4eabe1 100644 --- a/src/infra/stats_tops.erl +++ b/src/infra/stats_tops.erl @@ -81,7 +81,12 @@ ready() -> foreach_table(Table, Pattern, Fun) -> case lists:member(Table, mnesia:system_info(tables)) of - true -> lists:foreach(Fun, mnesia:dirty_match_object(Pattern)); + true -> + try lists:foreach(Fun, mnesia:dirty_match_object(Pattern)) + catch + exit:{aborted, {no_exists, _}} -> ok; + error:{aborted, {no_exists, _}} -> ok + end; false -> ok end.