Improve the moudules and fix the bugs found in new test cases

- Fix the bug that emqx_mountpoint:unmount/2 will throw exception
- Add emqx_banned:info/1 for test cases
- Rename macro TRIE in emqx_trie module to TRIE_TAB
- Rename macro TRIE_NODE in emqx_trie module to TRIE_NODE_TAB
- Rename macro ROUTE in emqx_router module to ROUTE_TAB
This commit is contained in:
Feng Lee 2019-08-06 09:05:55 +08:00
parent 4afa02ee48
commit f60f127681
8 changed files with 90 additions and 72 deletions

View File

@ -31,7 +31,7 @@
[{deps, [{deps,
[{meck, "0.8.13"}, % hex [{meck, "0.8.13"}, % hex
{bbmustache, "1.7.0"}, % hex {bbmustache, "1.7.0"}, % hex
{emqx_ct_helpers, "1.1.3"} % hex {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {branch, "develop"}}}
]} ]}
]} ]}
]}. ]}.

View File

@ -32,9 +32,10 @@
-export([start_link/0]). -export([start_link/0]).
-export([ add/1 -export([ check/1
, add/1
, delete/1 , delete/1
, check/1 , info/1
]). ]).
%% gen_server callbacks %% gen_server callbacks
@ -81,7 +82,11 @@ add(Banned) when is_record(Banned, banned) ->
-spec(delete({client_id, emqx_types:client_id()} | -spec(delete({client_id, emqx_types:client_id()} |
{username, emqx_types:username()} | {username, emqx_types:username()} |
{peername, emqx_types:peername()}) -> ok). {peername, emqx_types:peername()}) -> ok).
delete(Key) -> mnesia:dirty_delete(?BANNED_TAB, Key). delete(Key) ->
mnesia:dirty_delete(?BANNED_TAB, Key).
info(InfoKey) ->
mnesia:table_info(?BANNED_TAB, InfoKey).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks

View File

@ -16,8 +16,11 @@
-module(emqx_inflight). -module(emqx_inflight).
-compile(inline).
%% APIs %% APIs
-export([ new/1 -export([ new/0
, new/1
, contain/2 , contain/2
, lookup/2 , lookup/2
, insert/3 , insert/3
@ -45,9 +48,8 @@
-define(Inflight(MaxSize, Tree), {inflight, MaxSize, (Tree)}). -define(Inflight(MaxSize, Tree), {inflight, MaxSize, (Tree)}).
%%-------------------------------------------------------------------- -spec(new() -> inflight()).
%% APIs new() -> new(0).
%%--------------------------------------------------------------------
-spec(new(non_neg_integer()) -> inflight()). -spec(new(non_neg_integer()) -> inflight()).
new(MaxSize) when MaxSize >= 0 -> new(MaxSize) when MaxSize >= 0 ->

View File

@ -16,6 +16,8 @@
-module(emqx_json). -module(emqx_json).
-compile(inline).
-export([ encode/1 -export([ encode/1
, encode/2 , encode/2
, safe_encode/1 , safe_encode/1
@ -32,7 +34,8 @@
encode(Term) -> encode(Term) ->
jsx:encode(Term). jsx:encode(Term).
-spec(encode(jsx:json_term(), jsx_to_json:config()) -> jsx:json_text()). -spec(encode(jsx:json_term(), jsx_to_json:config())
-> jsx:json_text()).
encode(Term, Opts) -> encode(Term, Opts) ->
jsx:encode(Term, Opts). jsx:encode(Term, Opts).
@ -55,7 +58,8 @@ safe_encode(Term, Opts) ->
decode(Json) -> decode(Json) ->
jsx:decode(Json). jsx:decode(Json).
-spec(decode(jsx:json_text(), jsx_to_json:config()) -> jsx:json_term()). -spec(decode(jsx:json_text(), jsx_to_json:config())
-> jsx:json_term()).
decode(Json, Opts) -> decode(Json, Opts) ->
jsx:decode(Json, Opts). jsx:decode(Json, Opts).

View File

@ -17,7 +17,7 @@
-module(emqx_mountpoint). -module(emqx_mountpoint).
-include("emqx.hrl"). -include("emqx.hrl").
-include("logger.hrl"). -include("types.hrl").
-export([ mount/2 -export([ mount/2
, unmount/2 , unmount/2
@ -29,41 +29,46 @@
-type(mountpoint() :: binary()). -type(mountpoint() :: binary()).
%%-------------------------------------------------------------------- -spec(mount(maybe(mountpoint()), Any) -> Any
%% APIs when Any :: emqx_types:topic()
%%-------------------------------------------------------------------- | emqx_types:message()
| emqx_types:topic_filters()).
mount(undefined, Any) -> mount(undefined, Any) ->
Any; Any;
mount(MountPoint, Topic) when is_binary(Topic) -> mount(MountPoint, Topic) when is_binary(Topic) ->
<<MountPoint/binary, Topic/binary>>; prefix(MountPoint, Topic);
mount(MountPoint, Msg = #message{topic = Topic}) -> mount(MountPoint, Msg = #message{topic = Topic}) ->
Msg#message{topic = <<MountPoint/binary, Topic/binary>>}; Msg#message{topic = prefix(MountPoint, Topic)};
mount(MountPoint, TopicFilters) when is_list(TopicFilters) -> mount(MountPoint, TopicFilters) when is_list(TopicFilters) ->
[{<<MountPoint/binary, Topic/binary>>, SubOpts} [{prefix(MountPoint, Topic), SubOpts} || {Topic, SubOpts} <- TopicFilters].
|| {Topic, SubOpts} <- TopicFilters].
unmount(undefined, Msg) -> %% @private
Msg; -compile({inline, [prefix/2]}).
%% TODO: Fixme later prefix(MountPoint, Topic) ->
<<MountPoint/binary, Topic/binary>>.
-spec(unmount(maybe(mountpoint()), Any) -> Any
when Any :: emqx_types:topic()
| emqx_types:message()).
unmount(undefined, Any) ->
Any;
unmount(MountPoint, Topic) when is_binary(Topic) -> unmount(MountPoint, Topic) when is_binary(Topic) ->
try split_binary(Topic, byte_size(MountPoint)) of case string:prefix(Topic, MountPoint) of
{MountPoint, Topic1} -> Topic1 nomatch -> Topic;
catch Topic1 -> Topic1
error:badarg-> Topic
end; end;
unmount(MountPoint, Msg = #message{topic = Topic}) -> unmount(MountPoint, Msg = #message{topic = Topic}) ->
try split_binary(Topic, byte_size(MountPoint)) of case string:prefix(Topic, MountPoint) of
{MountPoint, Topic1} -> Msg#message{topic = Topic1} nomatch -> Msg;
catch Topic1 -> Msg#message{topic = Topic1}
error:badarg->
Msg
end. end.
-spec(replvar(maybe(mountpoint()), map()) -> maybe(mountpoint())).
replvar(undefined, _Vars) -> replvar(undefined, _Vars) ->
undefined; undefined;
replvar(MountPoint, #{client_id := ClientId, username := Username}) -> replvar(MountPoint, #{client_id := ClientId, username := Username}) ->
lists:foldl(fun feed_var/2, MountPoint, [{<<"%c">>, ClientId}, {<<"%u">>, Username}]). lists:foldl(fun feed_var/2, MountPoint,
[{<<"%c">>, ClientId}, {<<"%u">>, Username}]).
feed_var({<<"%c">>, ClientId}, MountPoint) -> feed_var({<<"%c">>, ClientId}, MountPoint) ->
emqx_topic:feed_var(<<"%c">>, ClientId, MountPoint); emqx_topic:feed_var(<<"%c">>, ClientId, MountPoint);

View File

@ -163,5 +163,7 @@ connack_error(banned) -> ?RC_BANNED;
connack_error(bad_authentication_method) -> ?RC_BAD_AUTHENTICATION_METHOD; connack_error(bad_authentication_method) -> ?RC_BAD_AUTHENTICATION_METHOD;
connack_error(_) -> ?RC_NOT_AUTHORIZED. connack_error(_) -> ?RC_NOT_AUTHORIZED.
%%TODO: This function should be removed.
puback([]) -> ?RC_NO_MATCHING_SUBSCRIBERS; puback([]) -> ?RC_NO_MATCHING_SUBSCRIBERS;
puback(L) when is_list(L) -> ?RC_SUCCESS. puback(L) when is_list(L) -> ?RC_SUCCESS.

View File

@ -66,16 +66,16 @@
-type(group() :: binary()). -type(group() :: binary()).
-type(destination() :: node() | {group(), node()}). -type(dest() :: node() | {group(), node()}).
-define(ROUTE, emqx_route). -define(ROUTE_TAB, emqx_route).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
mnesia(boot) -> mnesia(boot) ->
ok = ekka_mnesia:create_table(?ROUTE, [ ok = ekka_mnesia:create_table(?ROUTE_TAB, [
{type, bag}, {type, bag},
{ram_copies, [node()]}, {ram_copies, [node()]},
{record_name, route}, {record_name, route},
@ -83,7 +83,7 @@ mnesia(boot) ->
{storage_properties, [{ets, [{read_concurrency, true}, {storage_properties, [{ets, [{read_concurrency, true},
{write_concurrency, true}]}]}]); {write_concurrency, true}]}]}]);
mnesia(copy) -> mnesia(copy) ->
ok = ekka_mnesia:copy_table(?ROUTE). ok = ekka_mnesia:copy_table(?ROUTE_TAB).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Start a router %% Start a router
@ -102,7 +102,7 @@ start_link(Pool, Id) ->
add_route(Topic) when is_binary(Topic) -> add_route(Topic) when is_binary(Topic) ->
add_route(Topic, node()). add_route(Topic, node()).
-spec(add_route(emqx_topic:topic(), destination()) -> ok | {error, term()}). -spec(add_route(emqx_topic:topic(), dest()) -> ok | {error, term()}).
add_route(Topic, Dest) when is_binary(Topic) -> add_route(Topic, Dest) when is_binary(Topic) ->
call(pick(Topic), {add_route, Topic, Dest}). call(pick(Topic), {add_route, Topic, Dest}).
@ -110,7 +110,7 @@ add_route(Topic, Dest) when is_binary(Topic) ->
do_add_route(Topic) when is_binary(Topic) -> do_add_route(Topic) when is_binary(Topic) ->
do_add_route(Topic, node()). do_add_route(Topic, node()).
-spec(do_add_route(emqx_topic:topic(), destination()) -> ok | {error, term()}). -spec(do_add_route(emqx_topic:topic(), dest()) -> ok | {error, term()}).
do_add_route(Topic, Dest) when is_binary(Topic) -> do_add_route(Topic, Dest) when is_binary(Topic) ->
Route = #route{topic = Topic, dest = Dest}, Route = #route{topic = Topic, dest = Dest},
case lists:member(Route, lookup_routes(Topic)) of case lists:member(Route, lookup_routes(Topic)) of
@ -142,17 +142,17 @@ match_trie(Topic) ->
-spec(lookup_routes(emqx_topic:topic()) -> [emqx_types:route()]). -spec(lookup_routes(emqx_topic:topic()) -> [emqx_types:route()]).
lookup_routes(Topic) -> lookup_routes(Topic) ->
ets:lookup(?ROUTE, Topic). ets:lookup(?ROUTE_TAB, Topic).
-spec(has_routes(emqx_topic:topic()) -> boolean()). -spec(has_routes(emqx_topic:topic()) -> boolean()).
has_routes(Topic) when is_binary(Topic) -> has_routes(Topic) when is_binary(Topic) ->
ets:member(?ROUTE, Topic). ets:member(?ROUTE_TAB, Topic).
-spec(delete_route(emqx_topic:topic()) -> ok | {error, term()}). -spec(delete_route(emqx_topic:topic()) -> ok | {error, term()}).
delete_route(Topic) when is_binary(Topic) -> delete_route(Topic) when is_binary(Topic) ->
delete_route(Topic, node()). delete_route(Topic, node()).
-spec(delete_route(emqx_topic:topic(), destination()) -> ok | {error, term()}). -spec(delete_route(emqx_topic:topic(), dest()) -> ok | {error, term()}).
delete_route(Topic, Dest) when is_binary(Topic) -> delete_route(Topic, Dest) when is_binary(Topic) ->
call(pick(Topic), {delete_route, Topic, Dest}). call(pick(Topic), {delete_route, Topic, Dest}).
@ -160,7 +160,7 @@ delete_route(Topic, Dest) when is_binary(Topic) ->
do_delete_route(Topic) when is_binary(Topic) -> do_delete_route(Topic) when is_binary(Topic) ->
do_delete_route(Topic, node()). do_delete_route(Topic, node()).
-spec(do_delete_route(emqx_topic:topic(), destination()) -> ok | {error, term()}). -spec(do_delete_route(emqx_topic:topic(), dest()) -> ok | {error, term()}).
do_delete_route(Topic, Dest) -> do_delete_route(Topic, Dest) ->
Route = #route{topic = Topic, dest = Dest}, Route = #route{topic = Topic, dest = Dest},
case emqx_topic:wildcard(Topic) of case emqx_topic:wildcard(Topic) of
@ -170,7 +170,7 @@ do_delete_route(Topic, Dest) ->
-spec(topics() -> list(emqx_topic:topic())). -spec(topics() -> list(emqx_topic:topic())).
topics() -> topics() ->
mnesia:dirty_all_keys(?ROUTE). mnesia:dirty_all_keys(?ROUTE_TAB).
%% @doc Print routes to a topic %% @doc Print routes to a topic
-spec(print_routes(emqx_topic:topic()) -> ok). -spec(print_routes(emqx_topic:topic()) -> ok).
@ -224,25 +224,25 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
insert_direct_route(Route) -> insert_direct_route(Route) ->
mnesia:async_dirty(fun mnesia:write/3, [?ROUTE, Route, sticky_write]). mnesia:async_dirty(fun mnesia:write/3, [?ROUTE_TAB, Route, sticky_write]).
insert_trie_route(Route = #route{topic = Topic}) -> insert_trie_route(Route = #route{topic = Topic}) ->
case mnesia:wread({?ROUTE, Topic}) of case mnesia:wread({?ROUTE_TAB, Topic}) of
[] -> emqx_trie:insert(Topic); [] -> emqx_trie:insert(Topic);
_ -> ok _ -> ok
end, end,
mnesia:write(?ROUTE, Route, sticky_write). mnesia:write(?ROUTE_TAB, Route, sticky_write).
delete_direct_route(Route) -> delete_direct_route(Route) ->
mnesia:async_dirty(fun mnesia:delete_object/3, [?ROUTE, Route, sticky_write]). mnesia:async_dirty(fun mnesia:delete_object/3, [?ROUTE_TAB, Route, sticky_write]).
delete_trie_route(Route = #route{topic = Topic}) -> delete_trie_route(Route = #route{topic = Topic}) ->
case mnesia:wread({?ROUTE, Topic}) of case mnesia:wread({?ROUTE_TAB, Topic}) of
[Route] -> %% Remove route and trie [Route] -> %% Remove route and trie
ok = mnesia:delete_object(?ROUTE, Route, sticky_write), ok = mnesia:delete_object(?ROUTE_TAB, Route, sticky_write),
emqx_trie:delete(Topic); emqx_trie:delete(Topic);
[_|_] -> %% Remove route only [_|_] -> %% Remove route only
mnesia:delete_object(?ROUTE, Route, sticky_write); mnesia:delete_object(?ROUTE_TAB, Route, sticky_write);
[] -> ok [] -> ok
end. end.

View File

@ -34,8 +34,8 @@
-export([empty/0]). -export([empty/0]).
%% Mnesia tables %% Mnesia tables
-define(TRIE, emqx_trie). -define(TRIE_TAB, emqx_trie).
-define(TRIE_NODE, emqx_trie_node). -define(TRIE_NODE_TAB, emqx_trie_node).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
@ -48,13 +48,13 @@ mnesia(boot) ->
StoreProps = [{ets, [{read_concurrency, true}, StoreProps = [{ets, [{read_concurrency, true},
{write_concurrency, true}]}], {write_concurrency, true}]}],
%% Trie table %% Trie table
ok = ekka_mnesia:create_table(?TRIE, [ ok = ekka_mnesia:create_table(?TRIE_TAB, [
{ram_copies, [node()]}, {ram_copies, [node()]},
{record_name, trie}, {record_name, trie},
{attributes, record_info(fields, trie)}, {attributes, record_info(fields, trie)},
{storage_properties, StoreProps}]), {storage_properties, StoreProps}]),
%% Trie node table %% Trie node table
ok = ekka_mnesia:create_table(?TRIE_NODE, [ ok = ekka_mnesia:create_table(?TRIE_NODE_TAB, [
{ram_copies, [node()]}, {ram_copies, [node()]},
{record_name, trie_node}, {record_name, trie_node},
{attributes, record_info(fields, trie_node)}, {attributes, record_info(fields, trie_node)},
@ -62,9 +62,9 @@ mnesia(boot) ->
mnesia(copy) -> mnesia(copy) ->
%% Copy trie table %% Copy trie table
ok = ekka_mnesia:copy_table(?TRIE), ok = ekka_mnesia:copy_table(?TRIE_TAB),
%% Copy trie_node table %% Copy trie_node table
ok = ekka_mnesia:copy_table(?TRIE_NODE). ok = ekka_mnesia:copy_table(?TRIE_NODE_TAB).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Trie APIs %% Trie APIs
@ -73,7 +73,7 @@ mnesia(copy) ->
%% @doc Insert a topic filter into the trie. %% @doc Insert a topic filter into the trie.
-spec(insert(emqx_topic:topic()) -> ok). -spec(insert(emqx_topic:topic()) -> ok).
insert(Topic) when is_binary(Topic) -> insert(Topic) when is_binary(Topic) ->
case mnesia:wread({?TRIE_NODE, Topic}) of case mnesia:wread({?TRIE_NODE_TAB, Topic}) of
[#trie_node{topic = Topic}] -> [#trie_node{topic = Topic}] ->
ok; ok;
[TrieNode = #trie_node{topic = undefined}] -> [TrieNode = #trie_node{topic = undefined}] ->
@ -94,14 +94,14 @@ match(Topic) when is_binary(Topic) ->
%% @doc Lookup a trie node. %% @doc Lookup a trie node.
-spec(lookup(NodeId :: binary()) -> [#trie_node{}]). -spec(lookup(NodeId :: binary()) -> [#trie_node{}]).
lookup(NodeId) -> lookup(NodeId) ->
mnesia:read(?TRIE_NODE, NodeId). mnesia:read(?TRIE_NODE_TAB, NodeId).
%% @doc Delete a topic filter from the trie. %% @doc Delete a topic filter from the trie.
-spec(delete(emqx_topic:topic()) -> ok). -spec(delete(emqx_topic:topic()) -> ok).
delete(Topic) when is_binary(Topic) -> delete(Topic) when is_binary(Topic) ->
case mnesia:wread({?TRIE_NODE, Topic}) of case mnesia:wread({?TRIE_NODE_TAB, Topic}) of
[#trie_node{edge_count = 0}] -> [#trie_node{edge_count = 0}] ->
ok = mnesia:delete({?TRIE_NODE, Topic}), ok = mnesia:delete({?TRIE_NODE_TAB, Topic}),
delete_path(lists:reverse(emqx_topic:triples(Topic))); delete_path(lists:reverse(emqx_topic:triples(Topic)));
[TrieNode] -> [TrieNode] ->
write_trie_node(TrieNode#trie_node{topic = undefined}); write_trie_node(TrieNode#trie_node{topic = undefined});
@ -111,7 +111,7 @@ delete(Topic) when is_binary(Topic) ->
%% @doc Is the trie empty? %% @doc Is the trie empty?
-spec(empty() -> boolean()). -spec(empty() -> boolean()).
empty() -> empty() ->
ets:info(?TRIE, size) == 0. ets:info(?TRIE_TAB, size) == 0.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
@ -121,9 +121,9 @@ empty() ->
%% @doc Add a path to the trie. %% @doc Add a path to the trie.
add_path({Node, Word, Child}) -> add_path({Node, Word, Child}) ->
Edge = #trie_edge{node_id = Node, word = Word}, Edge = #trie_edge{node_id = Node, word = Word},
case mnesia:wread({?TRIE_NODE, Node}) of case mnesia:wread({?TRIE_NODE_TAB, Node}) of
[TrieNode = #trie_node{edge_count = Count}] -> [TrieNode = #trie_node{edge_count = Count}] ->
case mnesia:wread({?TRIE, Edge}) of case mnesia:wread({?TRIE_TAB, Edge}) of
[] -> [] ->
ok = write_trie_node(TrieNode#trie_node{edge_count = Count + 1}), ok = write_trie_node(TrieNode#trie_node{edge_count = Count + 1}),
write_trie(#trie{edge = Edge, node_id = Child}); write_trie(#trie{edge = Edge, node_id = Child});
@ -143,11 +143,11 @@ match_node(NodeId, Words) ->
match_node(NodeId, Words, []). match_node(NodeId, Words, []).
match_node(NodeId, [], ResAcc) -> match_node(NodeId, [], ResAcc) ->
mnesia:read(?TRIE_NODE, NodeId) ++ 'match_#'(NodeId, ResAcc); mnesia:read(?TRIE_NODE_TAB, NodeId) ++ 'match_#'(NodeId, ResAcc);
match_node(NodeId, [W|Words], ResAcc) -> match_node(NodeId, [W|Words], ResAcc) ->
lists:foldl(fun(WArg, Acc) -> lists:foldl(fun(WArg, Acc) ->
case mnesia:read(?TRIE, #trie_edge{node_id = NodeId, word = WArg}) of case mnesia:read(?TRIE_TAB, #trie_edge{node_id = NodeId, word = WArg}) of
[#trie{node_id = ChildId}] -> match_node(ChildId, Words, Acc); [#trie{node_id = ChildId}] -> match_node(ChildId, Words, Acc);
[] -> Acc [] -> Acc
end end
@ -156,9 +156,9 @@ match_node(NodeId, [W|Words], ResAcc) ->
%% @private %% @private
%% @doc Match node with '#'. %% @doc Match node with '#'.
'match_#'(NodeId, ResAcc) -> 'match_#'(NodeId, ResAcc) ->
case mnesia:read(?TRIE, #trie_edge{node_id = NodeId, word = '#'}) of case mnesia:read(?TRIE_TAB, #trie_edge{node_id = NodeId, word = '#'}) of
[#trie{node_id = ChildId}] -> [#trie{node_id = ChildId}] ->
mnesia:read(?TRIE_NODE, ChildId) ++ ResAcc; mnesia:read(?TRIE_NODE_TAB, ChildId) ++ ResAcc;
[] -> ResAcc [] -> ResAcc
end. end.
@ -167,10 +167,10 @@ match_node(NodeId, [W|Words], ResAcc) ->
delete_path([]) -> delete_path([]) ->
ok; ok;
delete_path([{NodeId, Word, _} | RestPath]) -> delete_path([{NodeId, Word, _} | RestPath]) ->
ok = mnesia:delete({?TRIE, #trie_edge{node_id = NodeId, word = Word}}), ok = mnesia:delete({?TRIE_TAB, #trie_edge{node_id = NodeId, word = Word}}),
case mnesia:wread({?TRIE_NODE, NodeId}) of case mnesia:wread({?TRIE_NODE_TAB, NodeId}) of
[#trie_node{edge_count = 1, topic = undefined}] -> [#trie_node{edge_count = 1, topic = undefined}] ->
ok = mnesia:delete({?TRIE_NODE, NodeId}), ok = mnesia:delete({?TRIE_NODE_TAB, NodeId}),
delete_path(RestPath); delete_path(RestPath);
[TrieNode = #trie_node{edge_count = 1, topic = _}] -> [TrieNode = #trie_node{edge_count = 1, topic = _}] ->
write_trie_node(TrieNode#trie_node{edge_count = 0}); write_trie_node(TrieNode#trie_node{edge_count = 0});
@ -182,9 +182,9 @@ delete_path([{NodeId, Word, _} | RestPath]) ->
%% @private %% @private
write_trie(Trie) -> write_trie(Trie) ->
mnesia:write(?TRIE, Trie, write). mnesia:write(?TRIE_TAB, Trie, write).
%% @private %% @private
write_trie_node(TrieNode) -> write_trie_node(TrieNode) ->
mnesia:write(?TRIE_NODE, TrieNode, write). mnesia:write(?TRIE_NODE_TAB, TrieNode, write).