Настройка хранения и создание индексов #13

This commit is contained in:
2026-05-02 23:18:25 +03:00
parent 8b2c55f425
commit 4fdf380f15
3 changed files with 204 additions and 114 deletions

View File

@@ -1,17 +1,31 @@
%% ===================================================================
%% EventHub infra_mnesia (стабильная версия с автоочисткой при fresh старте)
%% ===================================================================
-module(infra_mnesia).
-behaviour(gen_server).
-include("records.hrl").
%% API
-export([start_link/0, init_tables/0, wait_for_tables/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(TABLES, [
user, session, admin, admin_session, calendar, calendar_share, event, recurrence_exception,
booking, review, report, banned_word, ticket, subscription, admin_audit
user, session, admin, admin_session,
calendar, calendar_share, calendar_specialist,
event, recurrence_exception,
booking,
review, report, banned_word,
ticket, subscription,
admin_audit, notification
]).
-define(TABLE_WAIT_TIMEOUT, 5000).
%% ===================================================================
%% API
%% ===================================================================
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
@@ -21,125 +35,123 @@ init_tables() ->
wait_for_tables() ->
gen_server:call(?MODULE, wait_for_tables).
%% ===================================================================
%% gen_server callbacks
%% ===================================================================
init([]) ->
{ok, #{}}.
handle_call(init_tables, _From, State) ->
ok = ensure_schema(),
ok = maybe_recreate_schema(),
ok = ensure_cluster_join(),
lists:foreach(fun create_table/1, ?TABLES),
ok = create_indices(),
{reply, ok, State};
handle_call(wait_for_tables, _From, State) ->
mnesia:wait_for_tables(?TABLES, 5000),
mnesia:wait_for_tables(?TABLES, ?TABLE_WAIT_TIMEOUT),
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
handle_info(_Info, State) ->
{noreply, State}.
%% ===================================================================
%% Проверка директории Mnesia и при необходимости пересоздание схемы
%% ===================================================================
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% Internal functions
ensure_schema() ->
case mnesia:create_schema([node()]) of
ok ->
maybe_recreate_schema() ->
MnesiaDir = mnesia:system_info(directory),
case filelib:is_dir(MnesiaDir) of
false ->
io:format("Mnesia directory not found. Creating fresh schema...~n"),
mnesia:stop(),
mnesia:delete_schema([node()]),
mnesia:create_schema([node()]),
mnesia:start(),
ok;
{error, {Node, {already_exists, Node}}} ->
ok;
{error, {already_exists, _Node}} ->
ok;
{error, Reason} ->
error({schema_creation_failed, Reason})
true ->
io:format("Mnesia directory exists (~s). Reusing existing schema.~n", [MnesiaDir]),
case mnesia:system_info(is_running) of
yes -> ok;
_ -> mnesia:start()
end
end.
%% ===================================================================
%% Кластер
%% ===================================================================
ensure_cluster_join() ->
ExtraNodes = application:get_env(eventhub, extra_db_nodes, []),
case ExtraNodes of
[] -> ok;
Nodes ->
ok = mnesia:change_config(extra_db_nodes, Nodes),
case lists:member(node(), mnesia:table_info(schema, disc_copies)) of
false -> mnesia:add_table_copy(schema, node(), disc_copies);
true -> ok
end
end.
%% ===================================================================
%% Создание таблиц
%% ===================================================================
create_table(Table) ->
case mnesia:create_table(Table, table_opts(Table)) of
{atomic, ok} ->
ok;
{aborted, {already_exists, _}} ->
ok; % таблица уже существует пропускаем
Opts = table_opts(Table),
case mnesia:create_table(Table, Opts) of
{atomic, ok} -> ok;
{aborted, {already_exists, _}} -> ok;
{aborted, Reason} ->
error({table_creation_failed, Table, Reason})
end.
%% Опции таблиц без индексов (добавим позже)
table_opts(user) ->
[
{attributes, record_info(fields, user)},
{ram_copies, [node()]}
];
table_opts(session) ->
[
{attributes, record_info(fields, session)},
{ram_copies, [node()]}
];
table_opts(admin) ->
[
{attributes, record_info(fields, admin)},
{ram_copies, [node()]}
];
table_opts(admin_session) ->
[
{attributes, record_info(fields, admin_session)},
{ram_copies, [node()]}
];
table_opts(calendar) ->
[
{attributes, record_info(fields, calendar)},
{ram_copies, [node()]}
];
table_opts(calendar_share) ->
[
{attributes, record_info(fields, calendar_share)},
{ram_copies, [node()]}
];
table_opts(event) ->
[
{attributes, record_info(fields, event)},
{ram_copies, [node()]}
];
table_opts(recurrence_exception) ->
[
{attributes, record_info(fields, recurrence_exception)},
{ram_copies, [node()]}
];
table_opts(booking) ->
[
{attributes, record_info(fields, booking)},
{ram_copies, [node()]}
];
table_opts(review) ->
[
{attributes, record_info(fields, review)},
{ram_copies, [node()]}
];
table_opts(report) ->
[
{attributes, record_info(fields, report)},
{ram_copies, [node()]}
];
table_opts(banned_word) ->
[
{attributes, record_info(fields, banned_word)},
{ram_copies, [node()]}
];
table_opts(ticket) ->
[
{attributes, record_info(fields, ticket)},
{ram_copies, [node()]}
];
table_opts(subscription) ->
[
{attributes, record_info(fields, subscription)},
{ram_copies, [node()]}
];
table_opts(admin_audit) ->
[
{attributes, record_info(fields, admin_audit)},
{ram_copies, [node()]}
].
%% ===================================================================
%% Опции хранения таблиц
%% ===================================================================
table_opts(user) -> [{disc_copies, [node()]}, {attributes, record_info(fields, user)}];
table_opts(admin) -> [{disc_copies, [node()]}, {attributes, record_info(fields, admin)}];
table_opts(calendar) -> [{disc_copies, [node()]}, {attributes, record_info(fields, calendar)}];
table_opts(calendar_share) -> [{disc_copies, [node()]}, {attributes, record_info(fields, calendar_share)}];
table_opts(calendar_specialist) -> [{disc_copies, [node()]}, {attributes, record_info(fields, calendar_specialist)}];
table_opts(event) -> [{disc_copies, [node()]}, {attributes, record_info(fields, event)}];
table_opts(recurrence_exception) -> [{disc_copies, [node()]}, {attributes, record_info(fields, recurrence_exception)}];
table_opts(booking) -> [{disc_copies, [node()]}, {attributes, record_info(fields, booking)}];
table_opts(review) -> [{disc_copies, [node()]}, {attributes, record_info(fields, review)}];
table_opts(report) -> [{disc_copies, [node()]}, {attributes, record_info(fields, report)}];
table_opts(banned_word) -> [{disc_copies, [node()]}, {attributes, record_info(fields, banned_word)}];
table_opts(ticket) -> [{disc_copies, [node()]}, {attributes, record_info(fields, ticket)}];
table_opts(subscription) -> [{disc_copies, [node()]}, {attributes, record_info(fields, subscription)}];
table_opts(admin_audit) -> [{disc_copies, [node()]}, {attributes, record_info(fields, admin_audit)}];
table_opts(notification) -> [{disc_copies, [node()]}, {attributes, record_info(fields, notification)}];
table_opts(session) -> [{ram_copies, [node()]}, {attributes, record_info(fields, session)}];
table_opts(admin_session) -> [{ram_copies, [node()]}, {attributes, record_info(fields, admin_session)}].
%% ===================================================================
%% Индексы
%% ===================================================================
create_indices() ->
mnesia:add_table_index(event, calendar_id),
mnesia:add_table_index(event, start_time),
mnesia:add_table_index(event, event_type),
mnesia:add_table_index(event, master_id),
mnesia:add_table_index(event, specialist_id),
mnesia:add_table_index(event, status),
mnesia:add_table_index(booking, event_id),
mnesia:add_table_index(booking, user_id),
mnesia:add_table_index(booking, status),
mnesia:add_table_index(calendar, owner_id),
mnesia:add_table_index(calendar, status),
mnesia:add_table_index(calendar, short_name),
mnesia:add_table_index(calendar, category),
mnesia:add_table_index(calendar_specialist, calendar_id),
mnesia:add_table_index(calendar_specialist, user_id),
mnesia:add_table_index(user, nickname),
mnesia:add_table_index(notification, user_id),
mnesia:add_table_index(notification, is_read),
ok.