|
|
@@ -96,6 +96,12 @@
|
|
|
props := map(),
|
|
|
extra := map()
|
|
|
}.
|
|
|
+
|
|
|
+-define(TIMER_PULL, timer_pull).
|
|
|
+-define(TIMER_GET_STREAMS, timer_get_streams).
|
|
|
+-define(TIMER_BUMP_LAST_ALIVE_AT, timer_bump_last_alive_at).
|
|
|
+-type timer() :: ?TIMER_PULL | ?TIMER_GET_STREAMS | ?TIMER_BUMP_LAST_ALIVE_AT.
|
|
|
+
|
|
|
-type session() :: #{
|
|
|
%% Client ID
|
|
|
id := id(),
|
|
|
@@ -111,6 +117,8 @@
|
|
|
receive_maximum := pos_integer(),
|
|
|
%% Connection Info
|
|
|
conninfo := emqx_types:conninfo(),
|
|
|
+ %% Timers
|
|
|
+ timer() => reference(),
|
|
|
%%
|
|
|
props := map()
|
|
|
}.
|
|
|
@@ -120,7 +128,6 @@
|
|
|
-type clientinfo() :: emqx_types:clientinfo().
|
|
|
-type conninfo() :: emqx_session:conninfo().
|
|
|
-type replies() :: emqx_session:replies().
|
|
|
--type timer() :: pull | get_streams | bump_last_alive_at.
|
|
|
|
|
|
-define(STATS_KEYS, [
|
|
|
subscriptions_cnt,
|
|
|
@@ -144,8 +151,7 @@
|
|
|
session().
|
|
|
create(#{clientid := ClientID}, ConnInfo, Conf) ->
|
|
|
% TODO: expiration
|
|
|
- ensure_timers(),
|
|
|
- ensure_session(ClientID, ConnInfo, Conf).
|
|
|
+ ensure_timers(ensure_session(ClientID, ConnInfo, Conf)).
|
|
|
|
|
|
-spec open(clientinfo(), conninfo()) ->
|
|
|
{_IsPresent :: true, session(), []} | false.
|
|
|
@@ -159,10 +165,9 @@ open(#{clientid := ClientID} = _ClientInfo, ConnInfo) ->
|
|
|
ok = emqx_cm:discard_session(ClientID),
|
|
|
case session_open(ClientID, ConnInfo) of
|
|
|
Session0 = #{} ->
|
|
|
- ensure_timers(),
|
|
|
ReceiveMaximum = receive_maximum(ConnInfo),
|
|
|
Session = Session0#{receive_maximum => ReceiveMaximum},
|
|
|
- {true, Session, []};
|
|
|
+ {true, ensure_timers(Session), []};
|
|
|
false ->
|
|
|
false
|
|
|
end.
|
|
|
@@ -333,9 +338,9 @@ publish(_PacketId, Msg, Session) ->
|
|
|
puback(_ClientInfo, PacketId, Session = #{id := Id, inflight := Inflight0}) ->
|
|
|
case emqx_persistent_message_ds_replayer:commit_offset(Id, ack, PacketId, Inflight0) of
|
|
|
{true, Inflight} ->
|
|
|
- %% TODO
|
|
|
+ %% TODO: we pass a bogus message into the hook:
|
|
|
Msg = emqx_message:make(Id, <<>>, <<>>),
|
|
|
- {ok, Msg, [], Session#{inflight => Inflight}};
|
|
|
+ {ok, Msg, [], pull_now(Session#{inflight => Inflight})};
|
|
|
{false, _} ->
|
|
|
%% Invalid Packet Id
|
|
|
{error, ?RC_PACKET_IDENTIFIER_NOT_FOUND}
|
|
|
@@ -351,9 +356,9 @@ puback(_ClientInfo, PacketId, Session = #{id := Id, inflight := Inflight0}) ->
|
|
|
pubrec(PacketId, Session = #{id := Id, inflight := Inflight0}) ->
|
|
|
case emqx_persistent_message_ds_replayer:commit_offset(Id, rec, PacketId, Inflight0) of
|
|
|
{true, Inflight} ->
|
|
|
- %% TODO
|
|
|
+ %% TODO: we pass a bogus message into the hook:
|
|
|
Msg = emqx_message:make(Id, <<>>, <<>>),
|
|
|
- {ok, Msg, Session#{inflight => Inflight}};
|
|
|
+ {ok, Msg, pull_now(Session#{inflight => Inflight})};
|
|
|
{false, _} ->
|
|
|
%% Invalid Packet Id
|
|
|
{error, ?RC_PACKET_IDENTIFIER_NOT_FOUND}
|
|
|
@@ -399,9 +404,11 @@ deliver(_ClientInfo, _Delivers, Session) ->
|
|
|
{ok, replies(), session()} | {ok, replies(), timeout(), session()}.
|
|
|
handle_timeout(
|
|
|
_ClientInfo,
|
|
|
- pull,
|
|
|
- Session = #{id := Id, inflight := Inflight0, receive_maximum := ReceiveMaximum}
|
|
|
+ ?TIMER_PULL,
|
|
|
+ Session0 = #{id := Id, inflight := Inflight0, receive_maximum := ReceiveMaximum}
|
|
|
) ->
|
|
|
+ MaxBatchSize = emqx_config:get([session_persistence, max_batch_size]),
|
|
|
+ BatchSize = min(ReceiveMaximum, MaxBatchSize),
|
|
|
{Publishes, Inflight} = emqx_persistent_message_ds_replayer:poll(
|
|
|
fun
|
|
|
(_Seqno, Message = #message{qos = ?QOS_0}) ->
|
|
|
@@ -412,7 +419,7 @@ handle_timeout(
|
|
|
end,
|
|
|
Id,
|
|
|
Inflight0,
|
|
|
- ReceiveMaximum
|
|
|
+ BatchSize
|
|
|
),
|
|
|
IdlePollInterval = emqx_config:get([session_persistence, idle_poll_interval]),
|
|
|
Timeout =
|
|
|
@@ -422,13 +429,12 @@ handle_timeout(
|
|
|
[_ | _] ->
|
|
|
0
|
|
|
end,
|
|
|
- ensure_timer(pull, Timeout),
|
|
|
- {ok, Publishes, Session#{inflight := Inflight}};
|
|
|
-handle_timeout(_ClientInfo, get_streams, Session) ->
|
|
|
+ Session = emqx_session:ensure_timer(?TIMER_PULL, Timeout, Session0#{inflight := Inflight}),
|
|
|
+ {ok, Publishes, Session};
|
|
|
+handle_timeout(_ClientInfo, ?TIMER_GET_STREAMS, Session) ->
|
|
|
renew_streams(Session),
|
|
|
- ensure_timer(get_streams),
|
|
|
- {ok, [], Session};
|
|
|
-handle_timeout(_ClientInfo, bump_last_alive_at, Session0) ->
|
|
|
+ {ok, [], emqx_session:ensure_timer(?TIMER_GET_STREAMS, 100, Session)};
|
|
|
+handle_timeout(_ClientInfo, ?TIMER_BUMP_LAST_ALIVE_AT, Session0) ->
|
|
|
%% Note: we take a pessimistic approach here and assume that the client will be alive
|
|
|
%% until the next bump timeout. With this, we avoid garbage collecting this session
|
|
|
%% too early in case the session/connection/node crashes earlier without having time
|
|
|
@@ -436,8 +442,8 @@ handle_timeout(_ClientInfo, bump_last_alive_at, Session0) ->
|
|
|
BumpInterval = emqx_config:get([session_persistence, last_alive_update_interval]),
|
|
|
EstimatedLastAliveAt = now_ms() + BumpInterval,
|
|
|
Session = session_set_last_alive_at_trans(Session0, EstimatedLastAliveAt),
|
|
|
- ensure_timer(bump_last_alive_at),
|
|
|
- {ok, [], Session}.
|
|
|
+ BumpInterval = emqx_config:get([session_persistence, last_alive_update_interval]),
|
|
|
+ {ok, [], emqx_session:ensure_timer(?TIMER_BUMP_LAST_ALIVE_AT, BumpInterval, Session)}.
|
|
|
|
|
|
-spec replay(clientinfo(), [], session()) ->
|
|
|
{ok, replies(), session()}.
|
|
|
@@ -957,22 +963,15 @@ export_record(_, _, [], Acc) ->
|
|
|
|
|
|
%% TODO: find a more reliable way to perform actions that have side
|
|
|
%% effects. Add `CBM:init' callback to the session behavior?
|
|
|
-ensure_timers() ->
|
|
|
- ensure_timer(pull),
|
|
|
- ensure_timer(get_streams),
|
|
|
- ensure_timer(bump_last_alive_at).
|
|
|
-
|
|
|
--spec ensure_timer(timer()) -> ok.
|
|
|
-ensure_timer(bump_last_alive_at = Type) ->
|
|
|
- BumpInterval = emqx_config:get([session_persistence, last_alive_update_interval]),
|
|
|
- ensure_timer(Type, BumpInterval);
|
|
|
-ensure_timer(Type) ->
|
|
|
- ensure_timer(Type, 100).
|
|
|
-
|
|
|
--spec ensure_timer(timer(), non_neg_integer()) -> ok.
|
|
|
-ensure_timer(Type, Timeout) ->
|
|
|
- _ = emqx_utils:start_timer(Timeout, {emqx_session, Type}),
|
|
|
- ok.
|
|
|
+-spec ensure_timers(session()) -> session().
|
|
|
+ensure_timers(Session0) ->
|
|
|
+ Session1 = emqx_session:ensure_timer(?TIMER_PULL, 100, Session0),
|
|
|
+ Session2 = emqx_session:ensure_timer(?TIMER_GET_STREAMS, 100, Session1),
|
|
|
+ emqx_session:ensure_timer(?TIMER_BUMP_LAST_ALIVE_AT, 100, Session2).
|
|
|
+
|
|
|
+-spec pull_now(session()) -> session().
|
|
|
+pull_now(Session) ->
|
|
|
+ emqx_session:reset_timer(?TIMER_PULL, 0, Session).
|
|
|
|
|
|
-spec receive_maximum(conninfo()) -> pos_integer().
|
|
|
receive_maximum(ConnInfo) ->
|