diff --git a/src/iot_app.erl b/src/iot_app.erl index a24e8d7..c2c0865 100644 --- a/src/iot_app.erl +++ b/src/iot_app.erl @@ -14,8 +14,8 @@ start(_StartType, _StartArgs) -> %% 加速内存的回收 erlang:system_flag(fullsweep_after, 16), try - %% 启动mnesia数据库 - start_mnesia(), + %% 启动并校验已经初始化好的 mnesia 数据库 + ok = iot_mnesia:start(), %% 启动http服务。当前 supervisor 初始化依赖本机 simulator API,因此保留此顺序。 start_http_server(), @@ -43,17 +43,6 @@ stop(_State) -> %% internal functions -%% 启动内存数据库 -start_mnesia() -> - ok = ensure_mnesia_schema(), - %% 启动数据库 - ok = mnesia:start(), - ok = ensure_mnesia_tables(), - Tables = mnesia:system_info(tables), - logger:debug("[iot_app] tables: ~p", [Tables]), - %% 创建数据库表 - ok. - start_http_server() -> {ok, Props} = application:get_env(iot, http_server), Acceptors = proplists:get_value(acceptors, Props, 50), @@ -113,37 +102,5 @@ start_ssl_server() -> stop_started_services() -> _ = cowboy:stop_listener(http_listener), _ = ranch:stop_listener(ssl_server), - _ = mnesia:stop(), + _ = iot_mnesia:stop(), ok. - --spec ensure_mnesia_schema() -> any(). -ensure_mnesia_schema() -> - case mnesia:system_info(use_dir) of - true -> - ok; - false -> - mnesia:stop(), - case mnesia:create_schema([node()]) of - ok -> ok; - {error, {_, {already_exists, _}}} -> ok; - Error -> - logger:debug("[iot_app] create mnesia schema failed with error: ~p", [Error]), - throw({init_schema, Error}) - end - end. - --spec ensure_mnesia_tables() -> ok. -ensure_mnesia_tables() -> - ok = ensure_efka_client_table(), - ok = mnesia:wait_for_tables([efka_client], 5000), - ok. - --spec ensure_efka_client_table() -> ok. -ensure_efka_client_table() -> - case efka_client_store:ensure_table() of - ok -> - ok; - {error, Reason} -> - logger:debug("[iot_app] create efka_client table failed with error: ~p", [Reason]), - throw({init_tables, {efka_client, Reason}}) - end. \ No newline at end of file diff --git a/src/mnesia/iot_mnesia.erl b/src/mnesia/iot_mnesia.erl new file mode 100644 index 0000000..41805bc --- /dev/null +++ b/src/mnesia/iot_mnesia.erl @@ -0,0 +1,132 @@ +%%%------------------------------------------------------------------- +%%% @doc Mnesia bootstrap and runtime startup helpers. +%%% +%%% `init/0` is a manual, one-time bootstrap operation. Application +%%% startup must call `start/0`, which only starts an existing schema and +%%% verifies required tables. +%%%------------------------------------------------------------------- +-module(iot_mnesia). + +-include("iot_tables.hrl"). + +-export([init/0, start/0, stop/0]). + +-define(TABLES, [efka_client]). +-define(WAIT_TIMEOUT, 5000). + +-spec init() -> ok | {error, term()}. +init() -> + with_result(fun() -> + ok = ensure_persistent_dir(), + _ = mnesia:stop(), + ok = create_schema_if_missing(), + ok = start_mnesia(), + ok = create_tables(), + ok = wait_for_tables(), + ok = stop(), + ok + end). + +-spec start() -> ok | {error, term()}. +start() -> + with_result(fun() -> + ok = ensure_persistent_dir(), + ok = start_mnesia(), + ok = wait_for_tables(), + ok = verify_tables(), + log_runtime_info(), + ok + end). + +-spec stop() -> ok. +stop() -> + _ = mnesia:stop(), + ok. + +-spec ensure_persistent_dir() -> ok. +ensure_persistent_dir() -> + case mnesia:system_info(use_dir) of + true -> + ok; + false -> + throw({mnesia_dir_required, node()}) + end. + +-spec create_schema_if_missing() -> ok. +create_schema_if_missing() -> + case mnesia:create_schema([node()]) of + ok -> + ok; + {error, {_, {already_exists, _}}} -> + ok; + {error, Reason} -> + throw({create_schema_failed, Reason}) + end. + +-spec start_mnesia() -> ok. +start_mnesia() -> + case mnesia:start() of + ok -> + ok; + {error, {already_started, mnesia}} -> + ok; + {error, Reason} -> + throw({start_mnesia_failed, Reason}) + end. + +-spec create_tables() -> ok. +create_tables() -> + case efka_client_store:ensure_table() of + ok -> + ok; + {error, Reason} -> + throw({create_table_failed, {efka_client, Reason}}) + end. + +-spec wait_for_tables() -> ok. +wait_for_tables() -> + case mnesia:wait_for_tables(?TABLES, ?WAIT_TIMEOUT) of + ok -> + ok; + {timeout, Tables} -> + throw({mnesia_tables_timeout, Tables}); + {error, Reason} -> + throw({mnesia_tables_error, Reason}) + end. + +-spec verify_tables() -> ok. +verify_tables() -> + lists:foreach(fun verify_table/1, ?TABLES), + ok. + +-spec verify_table(atom()) -> ok. +verify_table(efka_client) -> + Expected = record_info(fields, efka_client), + case mnesia:table_info(efka_client, attributes) of + Expected -> + ok; + Actual -> + throw({table_attributes_mismatch, efka_client, Expected, Actual}) + end. + +-spec log_runtime_info() -> ok. +log_runtime_info() -> + logger:debug("[iot_mnesia] node: ~p, dir: ~p, tables: ~p", [ + node(), + mnesia:system_info(directory), + mnesia:system_info(tables) + ]), + ok. + +-spec with_result(fun(() -> ok)) -> ok | {error, term()}. +with_result(Fun) -> + try Fun() of + ok -> + ok + catch + throw:Reason -> + {error, Reason}; + Class:Reason:Stacktrace -> + logger:warning("[iot_mnesia] failed, class: ~p, reason: ~p, stack: ~p", [Class, Reason, Stacktrace]), + {error, Reason} + end. diff --git a/src/transport/tcp/ssl_channel.erl b/src/transport/tcp/ssl_channel.erl index 719ace6..bf9e73a 100644 --- a/src/transport/tcp/ssl_channel.erl +++ b/src/transport/tcp/ssl_channel.erl @@ -161,8 +161,9 @@ handle_info(Info, State) -> logger:warning("[ssl_channel] get a unknown message: ~p, state: ~p", [Info, State]), {noreply, State}. -terminate(Reason, #state{inflight = Inflight}) -> +terminate(Reason, #state{inflight = Inflight, transport = Transport, socket = Socket}) -> maps:foreach(fun(Ref, CommandInfo) -> reply_command_closed(Ref, CommandInfo, Reason) end, Inflight), + Transport:close(Socket), logger:warning("[ssl_channel] stop with reason: ~p", [Reason]), ok.