issue #282 - improve sm
This commit is contained in:
parent
d63f043566
commit
1935b414c7
|
@ -105,10 +105,9 @@
|
|||
%% MQTT Session
|
||||
%%------------------------------------------------------------------------------
|
||||
-record(mqtt_session, {
|
||||
client_id,
|
||||
sess_pid,
|
||||
persistent,
|
||||
on_node
|
||||
client_id :: binary(),
|
||||
sess_pid :: pid(),
|
||||
persistent :: boolean()
|
||||
}).
|
||||
|
||||
-type mqtt_session() :: #mqtt_session{}.
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
|
||||
-define(SM_POOL, ?MODULE).
|
||||
|
||||
%% TODO...
|
||||
-define(SESSION_TIMEOUT, 60000).
|
||||
|
||||
%%%=============================================================================
|
||||
|
@ -158,7 +157,7 @@ prioritise_cast(_Msg, _Len, _State) ->
|
|||
0.
|
||||
|
||||
prioritise_info(_Msg, _Len, _State) ->
|
||||
1.
|
||||
2.
|
||||
|
||||
%% persistent session
|
||||
handle_call({start_session, {false, ClientId, ClientPid}}, _From, State) ->
|
||||
|
@ -170,6 +169,7 @@ handle_call({start_session, {false, ClientId, ClientPid}}, _From, State) ->
|
|||
{reply, resume_session(Session, ClientPid), State}
|
||||
end;
|
||||
|
||||
%% transient session
|
||||
handle_call({start_session, {true, ClientId, ClientPid}}, _From, State) ->
|
||||
case lookup_session(ClientId) of
|
||||
undefined ->
|
||||
|
@ -216,12 +216,11 @@ create_session(CleanSess, ClientId, ClientPid) ->
|
|||
{ok, SessPid} ->
|
||||
Session = #mqtt_session{client_id = ClientId,
|
||||
sess_pid = SessPid,
|
||||
persistent = not CleanSess,
|
||||
on_node = node()},
|
||||
persistent = not CleanSess},
|
||||
case insert_session(Session) of
|
||||
{aborted, {conflict, Node}} ->
|
||||
%% conflict with othe node?
|
||||
lager:critical("Session ~s conflict with node ~p!", [ClientId, Node]),
|
||||
{aborted, {conflict, ConflictPid}} ->
|
||||
%% Conflict with othe node?
|
||||
lager:critical("Session(~s): Conflict with ~p!", [ClientId, ConflictPid]),
|
||||
{error, conflict};
|
||||
{atomic, ok} ->
|
||||
erlang:monitor(process, SessPid),
|
||||
|
@ -232,89 +231,65 @@ create_session(CleanSess, ClientId, ClientPid) ->
|
|||
end.
|
||||
|
||||
insert_session(Session = #mqtt_session{client_id = ClientId}) ->
|
||||
mnesia:transaction(fun() ->
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
case mnesia:wread({session, ClientId}) of
|
||||
[] ->
|
||||
mnesia:write(session, Session, write);
|
||||
[#mqtt_session{on_node = Node}] ->
|
||||
mnesia:abort({conflict, Node})
|
||||
[#mqtt_session{sess_pid = SessPid}] ->
|
||||
mnesia:abort({conflict, SessPid})
|
||||
end
|
||||
end).
|
||||
|
||||
%% local node
|
||||
%% Local node
|
||||
resume_session(#mqtt_session{client_id = ClientId,
|
||||
sess_pid = SessPid,
|
||||
on_node = Node}, ClientPid)
|
||||
when Node =:= node() ->
|
||||
case is_process_alive(SessPid) of
|
||||
true ->
|
||||
sess_pid = SessPid}, ClientPid)
|
||||
when node(SessPid) =:= node() ->
|
||||
|
||||
emqttd_session:resume(SessPid, ClientId, ClientPid),
|
||||
{ok, SessPid};
|
||||
false ->
|
||||
lager:critical("Session ~s@~p died unexpectedly!", [ClientId, SessPid]),
|
||||
{error, session_died}
|
||||
end;
|
||||
|
||||
%% remote node
|
||||
resume_session(Session = #mqtt_session{client_id = ClientId,
|
||||
sess_pid = SessPid,
|
||||
on_node = Node}, ClientPid) ->
|
||||
case emqttd:is_running(Node) of
|
||||
true ->
|
||||
%% Remote node
|
||||
resume_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid}, ClientPid) ->
|
||||
Node = node(SessPid),
|
||||
case rpc:call(Node, emqttd_session, resume, [SessPid, ClientId, ClientPid]) of
|
||||
ok ->
|
||||
{ok, SessPid};
|
||||
{badrpc, nodedown} ->
|
||||
lager:critical("Session(~s): Died for node ~s down!", [ClientId, Node]),
|
||||
remove_session(Session),
|
||||
{error, session_nodedown};
|
||||
{badrpc, Reason} ->
|
||||
lager:critical("Resume session ~s on remote node ~p failed for ~p",
|
||||
lager:critical("Session(~s): Failed to resume from node ~s for ~p",
|
||||
[ClientId, Node, Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
false ->
|
||||
lager:critical("Session ~s died for node ~p down!", [ClientId, Node]),
|
||||
remove_session(Session),
|
||||
{error, session_node_down}
|
||||
end.
|
||||
|
||||
%% local node
|
||||
destroy_session(Session = #mqtt_session{client_id = ClientId,
|
||||
sess_pid = SessPid,
|
||||
on_node = Node}) when Node =:= node() ->
|
||||
case is_process_alive(SessPid) of
|
||||
true ->
|
||||
emqttd_session:destroy(SessPid, ClientId);
|
||||
false ->
|
||||
lager:critical("Session ~s@~p died unexpectedly!", [ClientId, SessPid])
|
||||
end,
|
||||
case remove_session(Session) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Error} -> {error, Error}
|
||||
end;
|
||||
%% Local node
|
||||
destroy_session(Session = #mqtt_session{client_id = ClientId, sess_pid = SessPid})
|
||||
when node(SessPid) =:= node() ->
|
||||
emqttd_session:destroy(SessPid, ClientId),
|
||||
remove_session(Session);
|
||||
|
||||
%% remote node
|
||||
%% Remote node
|
||||
destroy_session(Session = #mqtt_session{client_id = ClientId,
|
||||
sess_pid = SessPid,
|
||||
on_node = Node}) ->
|
||||
case emqttd:is_running(Node) of
|
||||
true ->
|
||||
sess_pid = SessPid}) ->
|
||||
Node = node(SessPid),
|
||||
case rpc:call(Node, emqttd_session, destroy, [SessPid, ClientId]) of
|
||||
ok ->
|
||||
case remove_session(Session) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Error} -> {error, Error}
|
||||
end;
|
||||
remove_session(Session);
|
||||
{badrpc, nodedown} ->
|
||||
lager:error("Session(~s): Died for node ~s down!", [ClientId, Node]),
|
||||
remove_session(Session);
|
||||
{badrpc, Reason} ->
|
||||
lager:critical("Destroy session ~s on remote node ~p failed for ~p",
|
||||
[ClientId, Node, Reason]),
|
||||
{error, list_to_atom("session_" ++ atom_to_list(Reason))}
|
||||
end;
|
||||
false ->
|
||||
lager:error("Session ~s died for node ~p down!", [ClientId, Node]),
|
||||
case remove_session(Session) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Error} -> {error, Error}
|
||||
end
|
||||
lager:error("Session(~s): Failed to destory ~p on remote node ~p for ~s",
|
||||
[ClientId, SessPid, Node, Reason]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
remove_session(Session) ->
|
||||
mnesia:transaction(fun() -> mnesia:delete_object(session, Session, write) end).
|
||||
case mnesia:transaction(fun mnesia:delete_object/3, [session, Session, write]) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
%%% @end
|
||||
%%%-----------------------------------------------------------------------------
|
||||
|
||||
%% TODO: Monitor mnesia node down...
|
||||
|
||||
-module(emqttd_sm_helper).
|
||||
|
||||
-author("Feng Lee <feng@emqtt.io>").
|
||||
|
||||
-include("emqttd.hrl").
|
||||
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
%% API Function Exports
|
||||
-export([start_link/0]).
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
|||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {statsfun, ticker}).
|
||||
-record(state, {stats_fun, tick_tref}).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% @doc Start a session helper
|
||||
|
@ -53,9 +53,10 @@ start_link() ->
|
|||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
%%mnesia:subscribe(system),
|
||||
{ok, TRef} = timer:send_interval(1000, tick),
|
||||
StatsFun = emqttd_stats:statsfun('sessions/count', 'sessions/max'),
|
||||
{ok, TRef} = timer:send_interval(1000, self(), tick),
|
||||
{ok, #state{statsfun = StatsFun, ticker = TRef}}.
|
||||
{ok, #state{stats_fun = StatsFun, tick_tref = TRef}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
|
@ -64,20 +65,36 @@ handle_cast(Msg, State) ->
|
|||
lager:critical("Unexpected Msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
lager:error("!!!Mnesia node down: ~s", [Node]),
|
||||
Fun = fun() ->
|
||||
ClientIds =
|
||||
mnesia:select(session, [{#mqtt_session{client_id = '$1', sess_pid = '$2'},
|
||||
[{'==', {node, '$2'}, Node}],
|
||||
['$1']}]),
|
||||
lists:foreach(fun(Id) -> mnesia:delete({session, Id}) end, ClientIds)
|
||||
end,
|
||||
mnesia:async_dirty(Fun),
|
||||
{noreply, State};
|
||||
|
||||
handle_info(tick, State) ->
|
||||
{noreply, setstats(State)};
|
||||
{noreply, setstats(State), hibernate};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
lager:critical("Unexpected Info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State = #state{ticker = TRef}) ->
|
||||
timer:cancel(TRef).
|
||||
terminate(_Reason, _State = #state{tick_tref = TRef}) ->
|
||||
timer:cancel(TRef),
|
||||
mnesia:unsubscribe(system).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
setstats(State = #state{statsfun = StatsFun}) ->
|
||||
%%%=============================================================================
|
||||
%%% Internal functions
|
||||
%%%=============================================================================
|
||||
|
||||
setstats(State = #state{stats_fun = StatsFun}) ->
|
||||
StatsFun(ets:info(mqtt_persistent_session, size)), State.
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue