feat(queue): clarify naming; identify shared subs by full topic filter

This commit is contained in:
Ilya Averyanov 2024-07-10 19:02:15 +03:00
parent 7e23f8d19f
commit f213569460
9 changed files with 303 additions and 244 deletions

View File

@ -119,8 +119,8 @@ new(Opts) ->
{ok, emqx_persistent_session_ds_state:t(), t()}. {ok, emqx_persistent_session_ds_state:t(), t()}.
open(S, Opts) -> open(S, Opts) ->
SharedSubscriptions = fold_shared_subs( SharedSubscriptions = fold_shared_subs(
fun(#share{} = TopicFilter, Sub, Acc) -> fun(#share{} = ShareTopicFilter, Sub, Acc) ->
[{TopicFilter, to_agent_subscription(S, Sub)} | Acc] [{ShareTopicFilter, to_agent_subscription(S, Sub)} | Acc]
end, end,
[], [],
S S
@ -139,33 +139,33 @@ open(S, Opts) ->
emqx_types:subopts(), emqx_types:subopts(),
emqx_persistent_session_ds:session() emqx_persistent_session_ds:session()
) -> {ok, emqx_persistent_session_ds_state:t(), t()} | {error, emqx_types:reason_code()}. ) -> {ok, emqx_persistent_session_ds_state:t(), t()} | {error, emqx_types:reason_code()}.
on_subscribe(TopicFilter, SubOpts, #{s := S} = Session) -> on_subscribe(#share{} = ShareTopicFilter, SubOpts, #{s := S} = Session) ->
Subscription = emqx_persistent_session_ds_state:get_subscription(TopicFilter, S), Subscription = emqx_persistent_session_ds_state:get_subscription(ShareTopicFilter, S),
on_subscribe(Subscription, TopicFilter, SubOpts, Session). on_subscribe(Subscription, ShareTopicFilter, SubOpts, Session).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% on_subscribe internal functions %% on_subscribe internal functions
on_subscribe(undefined, TopicFilter, SubOpts, #{props := Props, s := S} = Session) -> on_subscribe(undefined, ShareTopicFilter, SubOpts, #{props := Props, s := S} = Session) ->
#{max_subscriptions := MaxSubscriptions} = Props, #{max_subscriptions := MaxSubscriptions} = Props,
case emqx_persistent_session_ds_state:n_subscriptions(S) < MaxSubscriptions of case emqx_persistent_session_ds_state:n_subscriptions(S) < MaxSubscriptions of
true -> true ->
create_new_subscription(TopicFilter, SubOpts, Session); create_new_subscription(ShareTopicFilter, SubOpts, Session);
false -> false ->
{error, ?RC_QUOTA_EXCEEDED} {error, ?RC_QUOTA_EXCEEDED}
end; end;
on_subscribe(Subscription, TopicFilter, SubOpts, Session) -> on_subscribe(Subscription, ShareTopicFilter, SubOpts, Session) ->
update_subscription(Subscription, TopicFilter, SubOpts, Session). update_subscription(Subscription, ShareTopicFilter, SubOpts, Session).
-dialyzer({nowarn_function, create_new_subscription/3}). -dialyzer({nowarn_function, create_new_subscription/3}).
create_new_subscription(TopicFilter, SubOpts, #{ create_new_subscription(ShareTopicFilter, SubOpts, #{
s := S0, s := S0,
shared_sub_s := #{agent := Agent} = SharedSubS0, shared_sub_s := #{agent := Agent} = SharedSubS0,
props := Props props := Props
}) -> }) ->
case case
emqx_persistent_session_ds_shared_subs_agent:can_subscribe( emqx_persistent_session_ds_shared_subs_agent:can_subscribe(
Agent, TopicFilter, SubOpts Agent, ShareTopicFilter, SubOpts
) )
of of
ok -> ok ->
@ -184,17 +184,19 @@ create_new_subscription(TopicFilter, SubOpts, #{
start_time => now_ms() start_time => now_ms()
}, },
S = emqx_persistent_session_ds_state:put_subscription( S = emqx_persistent_session_ds_state:put_subscription(
TopicFilter, Subscription, S3 ShareTopicFilter, Subscription, S3
), ),
SharedSubS = schedule_subscribe(SharedSubS0, TopicFilter, SubOpts), SharedSubS = schedule_subscribe(SharedSubS0, ShareTopicFilter, SubOpts),
{ok, S, SharedSubS}; {ok, S, SharedSubS};
{error, _} = Error -> {error, _} = Error ->
Error Error
end. end.
update_subscription(#{current_state := SStateId0, id := SubId} = Sub0, TopicFilter, SubOpts, #{ update_subscription(
s := S0, shared_sub_s := SharedSubS, props := Props #{current_state := SStateId0, id := SubId} = Sub0, ShareTopicFilter, SubOpts, #{
}) -> s := S0, shared_sub_s := SharedSubS, props := Props
}
) ->
#{upgrade_qos := UpgradeQoS} = Props, #{upgrade_qos := UpgradeQoS} = Props,
SState = #{parent_subscription => SubId, upgrade_qos => UpgradeQoS, subopts => SubOpts}, SState = #{parent_subscription => SubId, upgrade_qos => UpgradeQoS, subopts => SubOpts},
case emqx_persistent_session_ds_state:get_subscription_state(SStateId0, S0) of case emqx_persistent_session_ds_state:get_subscription_state(SStateId0, S0) of
@ -208,31 +210,33 @@ update_subscription(#{current_state := SStateId0, id := SubId} = Sub0, TopicFilt
SStateId, SState, S1 SStateId, SState, S1
), ),
Sub = Sub0#{current_state => SStateId}, Sub = Sub0#{current_state => SStateId},
S = emqx_persistent_session_ds_state:put_subscription(TopicFilter, Sub, S2), S = emqx_persistent_session_ds_state:put_subscription(ShareTopicFilter, Sub, S2),
{ok, S, SharedSubS} {ok, S, SharedSubS}
end. end.
-dialyzer({nowarn_function, schedule_subscribe/3}). -dialyzer({nowarn_function, schedule_subscribe/3}).
schedule_subscribe( schedule_subscribe(
#{agent := Agent0, scheduled_actions := ScheduledActions0} = SharedSubS0, TopicFilter, SubOpts #{agent := Agent0, scheduled_actions := ScheduledActions0} = SharedSubS0,
ShareTopicFilter,
SubOpts
) -> ) ->
case ScheduledActions0 of case ScheduledActions0 of
#{TopicFilter := ScheduledAction} -> #{ShareTopicFilter := ScheduledAction} ->
ScheduledActions1 = ScheduledActions0#{ ScheduledActions1 = ScheduledActions0#{
TopicFilter => ScheduledAction#{type => {?schedule_subscribe, SubOpts}} ShareTopicFilter => ScheduledAction#{type => {?schedule_subscribe, SubOpts}}
}, },
?tp(warning, shared_subs_schedule_subscribe_override, #{ ?tp(warning, shared_subs_schedule_subscribe_override, #{
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
new_type => {?schedule_subscribe, SubOpts}, new_type => {?schedule_subscribe, SubOpts},
old_action => format_schedule_action(ScheduledAction) old_action => format_schedule_action(ScheduledAction)
}), }),
SharedSubS0#{scheduled_actions := ScheduledActions1}; SharedSubS0#{scheduled_actions := ScheduledActions1};
_ -> _ ->
?tp(warning, shared_subs_schedule_subscribe_new, #{ ?tp(warning, shared_subs_schedule_subscribe_new, #{
topic_filter => TopicFilter, subopts => SubOpts share_topic_filter => ShareTopicFilter, subopts => SubOpts
}), }),
Agent1 = emqx_persistent_session_ds_shared_subs_agent:on_subscribe( Agent1 = emqx_persistent_session_ds_shared_subs_agent:on_subscribe(
Agent0, TopicFilter, SubOpts Agent0, ShareTopicFilter, SubOpts
), ),
SharedSubS0#{agent => Agent1} SharedSubS0#{agent => Agent1}
end. end.
@ -242,22 +246,22 @@ schedule_subscribe(
-spec on_unsubscribe( -spec on_unsubscribe(
emqx_persistent_session_ds:id(), emqx_persistent_session_ds:id(),
emqx_persistent_session_ds:topic_filter(), share_topic_filter(),
emqx_persistent_session_ds_state:t(), emqx_persistent_session_ds_state:t(),
t() t()
) -> ) ->
{ok, emqx_persistent_session_ds_state:t(), t(), emqx_persistent_session_ds:subscription()} {ok, emqx_persistent_session_ds_state:t(), t(), emqx_persistent_session_ds:subscription()}
| {error, emqx_types:reason_code()}. | {error, emqx_types:reason_code()}.
on_unsubscribe(SessionId, TopicFilter, S0, SharedSubS0) -> on_unsubscribe(SessionId, ShareTopicFilter, S0, SharedSubS0) ->
case lookup(TopicFilter, S0) of case lookup(ShareTopicFilter, S0) of
undefined -> undefined ->
{error, ?RC_NO_SUBSCRIPTION_EXISTED}; {error, ?RC_NO_SUBSCRIPTION_EXISTED};
#{id := SubId} = Subscription -> #{id := SubId} = Subscription ->
?tp(persistent_session_ds_subscription_delete, #{ ?tp(persistent_session_ds_subscription_delete, #{
session_id => SessionId, topic_filter => TopicFilter session_id => SessionId, share_topic_filter => ShareTopicFilter
}), }),
S = emqx_persistent_session_ds_state:del_subscription(TopicFilter, S0), S = emqx_persistent_session_ds_state:del_subscription(ShareTopicFilter, S0),
SharedSubS = schedule_unsubscribe(S, SharedSubS0, SubId, TopicFilter), SharedSubS = schedule_unsubscribe(S, SharedSubS0, SubId, ShareTopicFilter),
{ok, S, SharedSubS, Subscription} {ok, S, SharedSubS, Subscription}
end. end.
@ -265,16 +269,16 @@ on_unsubscribe(SessionId, TopicFilter, S0, SharedSubS0) ->
%% on_unsubscribe internal functions %% on_unsubscribe internal functions
schedule_unsubscribe( schedule_unsubscribe(
S, #{scheduled_actions := ScheduledActions0} = SharedSubS0, UnsubscridedSubId, TopicFilter S, #{scheduled_actions := ScheduledActions0} = SharedSubS0, UnsubscridedSubId, ShareTopicFilter
) -> ) ->
case ScheduledActions0 of case ScheduledActions0 of
#{TopicFilter := ScheduledAction0} -> #{ShareTopicFilter := ScheduledAction0} ->
ScheduledAction1 = ScheduledAction0#{type => ?schedule_unsubscribe}, ScheduledAction1 = ScheduledAction0#{type => ?schedule_unsubscribe},
ScheduledActions1 = ScheduledActions0#{ ScheduledActions1 = ScheduledActions0#{
TopicFilter => ScheduledAction1 ShareTopicFilter => ScheduledAction1
}, },
?tp(warning, shared_subs_schedule_unsubscribe_override, #{ ?tp(warning, shared_subs_schedule_unsubscribe_override, #{
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
new_type => ?schedule_unsubscribe, new_type => ?schedule_unsubscribe,
old_action => format_schedule_action(ScheduledAction0) old_action => format_schedule_action(ScheduledAction0)
}), }),
@ -282,14 +286,14 @@ schedule_unsubscribe(
_ -> _ ->
StreamKeys = stream_keys_by_sub_id(S, UnsubscridedSubId), StreamKeys = stream_keys_by_sub_id(S, UnsubscridedSubId),
ScheduledActions1 = ScheduledActions0#{ ScheduledActions1 = ScheduledActions0#{
TopicFilter => #{ ShareTopicFilter => #{
type => ?schedule_unsubscribe, type => ?schedule_unsubscribe,
stream_keys_to_wait => StreamKeys, stream_keys_to_wait => StreamKeys,
progresses => [] progresses => []
} }
}, },
?tp(warning, shared_subs_schedule_unsubscribe_new, #{ ?tp(warning, shared_subs_schedule_unsubscribe_new, #{
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
stream_keys => format_stream_keys(StreamKeys) stream_keys => format_stream_keys(StreamKeys)
}), }),
SharedSubS0#{scheduled_actions := ScheduledActions1} SharedSubS0#{scheduled_actions := ScheduledActions1}
@ -322,13 +326,13 @@ renew_streams(S0, #{agent := Agent0, scheduled_actions := ScheduledActions} = Sh
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% renew_streams internal functions %% renew_streams internal functions
accept_stream(#{topic_filter := TopicFilter} = Event, S, ScheduledActions) -> accept_stream(#{share_topic_filter := ShareTopicFilter} = Event, S, ScheduledActions) ->
%% If we have a pending action (subscribe or unsubscribe) for this topic filter, %% If we have a pending action (subscribe or unsubscribe) for this topic filter,
%% we should not accept a stream and start replaying it. We won't use it anyway: %% we should not accept a stream and start replaying it. We won't use it anyway:
%% * if subscribe is pending, we will reset agent obtain a new lease %% * if subscribe is pending, we will reset agent obtain a new lease
%% * if unsubscribe is pending, we will drop connection %% * if unsubscribe is pending, we will drop connection
case ScheduledActions of case ScheduledActions of
#{TopicFilter := _Action} -> #{ShareTopicFilter := _Action} ->
S; S;
_ -> _ ->
accept_stream(Event, S) accept_stream(Event, S)
@ -336,13 +340,13 @@ accept_stream(#{topic_filter := TopicFilter} = Event, S, ScheduledActions) ->
accept_stream( accept_stream(
#{ #{
topic_filter := TopicFilter, share_topic_filter := ShareTopicFilter,
stream := Stream, stream := Stream,
progress := #{iterator := Iterator} = _Progress progress := #{iterator := Iterator} = _Progress
} = _Event, } = _Event,
S0 S0
) -> ) ->
case emqx_persistent_session_ds_state:get_subscription(TopicFilter, S0) of case emqx_persistent_session_ds_state:get_subscription(ShareTopicFilter, S0) of
undefined -> undefined ->
%% We unsubscribed %% We unsubscribed
S0; S0;
@ -375,9 +379,9 @@ accept_stream(
end. end.
revoke_stream( revoke_stream(
#{topic_filter := TopicFilter, stream := Stream}, S0 #{share_topic_filter := ShareTopicFilter, stream := Stream}, S0
) -> ) ->
case emqx_persistent_session_ds_state:get_subscription(TopicFilter, S0) of case emqx_persistent_session_ds_state:get_subscription(ShareTopicFilter, S0) of
undefined -> undefined ->
%% This should not happen. %% This should not happen.
%% Agent should have received unsubscribe callback %% Agent should have received unsubscribe callback
@ -427,12 +431,7 @@ all_stream_progresses(S, _Agent, NeedUnacked) ->
CommQos1 = emqx_persistent_session_ds_state:get_seqno(?committed(?QOS_1), S), CommQos1 = emqx_persistent_session_ds_state:get_seqno(?committed(?QOS_1), S),
CommQos2 = emqx_persistent_session_ds_state:get_seqno(?committed(?QOS_2), S), CommQos2 = emqx_persistent_session_ds_state:get_seqno(?committed(?QOS_2), S),
fold_shared_stream_states( fold_shared_stream_states(
fun( fun(ShareTopicFilter, Stream, SRS, ProgressesAcc0) ->
#share{group = Group},
Stream,
SRS,
ProgressesAcc0
) ->
case case
is_stream_started(CommQos1, CommQos2, SRS) and is_stream_started(CommQos1, CommQos2, SRS) and
(NeedUnacked or is_stream_fully_acked(CommQos1, CommQos2, SRS)) (NeedUnacked or is_stream_fully_acked(CommQos1, CommQos2, SRS))
@ -440,7 +439,7 @@ all_stream_progresses(S, _Agent, NeedUnacked) ->
true -> true ->
StreamProgress = stream_progress(CommQos1, CommQos2, Stream, SRS), StreamProgress = stream_progress(CommQos1, CommQos2, Stream, SRS),
maps:update_with( maps:update_with(
Group, ShareTopicFilter,
fun(Progresses) -> [StreamProgress | Progresses] end, fun(Progresses) -> [StreamProgress | Progresses] end,
[StreamProgress], [StreamProgress],
ProgressesAcc0 ProgressesAcc0
@ -455,12 +454,12 @@ all_stream_progresses(S, _Agent, NeedUnacked) ->
run_scheduled_actions(S, Agent, ScheduledActions) -> run_scheduled_actions(S, Agent, ScheduledActions) ->
maps:fold( maps:fold(
fun(TopicFilter, Action0, {AgentAcc0, ScheduledActionsAcc}) -> fun(ShareTopicFilter, Action0, {AgentAcc0, ScheduledActionsAcc}) ->
case run_scheduled_action(S, AgentAcc0, TopicFilter, Action0) of case run_scheduled_action(S, AgentAcc0, ShareTopicFilter, Action0) of
{ok, AgentAcc1} -> {ok, AgentAcc1} ->
{AgentAcc1, maps:remove(TopicFilter, ScheduledActionsAcc)}; {AgentAcc1, maps:remove(ShareTopicFilter, ScheduledActionsAcc)};
{continue, Action1} -> {continue, Action1} ->
{AgentAcc0, ScheduledActionsAcc#{TopicFilter => Action1}} {AgentAcc0, ScheduledActionsAcc#{ShareTopicFilter => Action1}}
end end
end, end,
{Agent, ScheduledActions}, {Agent, ScheduledActions},
@ -470,7 +469,7 @@ run_scheduled_actions(S, Agent, ScheduledActions) ->
run_scheduled_action( run_scheduled_action(
S, S,
Agent0, Agent0,
#share{group = Group} = TopicFilter, ShareTopicFilter,
#{type := Type, stream_keys_to_wait := StreamKeysToWait0, progresses := Progresses0} = Action #{type := Type, stream_keys_to_wait := StreamKeysToWait0, progresses := Progresses0} = Action
) -> ) ->
StreamKeysToWait1 = filter_unfinished_streams(S, StreamKeysToWait0), StreamKeysToWait1 = filter_unfinished_streams(S, StreamKeysToWait0),
@ -478,31 +477,31 @@ run_scheduled_action(
case StreamKeysToWait1 of case StreamKeysToWait1 of
[] -> [] ->
?tp(warning, shared_subs_schedule_action_complete, #{ ?tp(warning, shared_subs_schedule_action_complete, #{
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
progresses => format_stream_progresses(Progresses1), progresses => format_stream_progresses(Progresses1),
type => Type type => Type
}), }),
%% Regular progress won't se unsubscribed streams, so we need to %% Regular progress won't se unsubscribed streams, so we need to
%% send the progress explicitly. %% send the progress explicitly.
Agent1 = emqx_persistent_session_ds_shared_subs_agent:on_stream_progress( Agent1 = emqx_persistent_session_ds_shared_subs_agent:on_stream_progress(
Agent0, #{Group => Progresses1} Agent0, #{ShareTopicFilter => Progresses1}
), ),
case Type of case Type of
{?schedule_subscribe, SubOpts} -> {?schedule_subscribe, SubOpts} ->
{ok, {ok,
emqx_persistent_session_ds_shared_subs_agent:on_subscribe( emqx_persistent_session_ds_shared_subs_agent:on_subscribe(
Agent1, TopicFilter, SubOpts Agent1, ShareTopicFilter, SubOpts
)}; )};
?schedule_unsubscribe -> ?schedule_unsubscribe ->
{ok, {ok,
emqx_persistent_session_ds_shared_subs_agent:on_unsubscribe( emqx_persistent_session_ds_shared_subs_agent:on_unsubscribe(
Agent1, TopicFilter, Progresses1 Agent1, ShareTopicFilter, Progresses1
)} )}
end; end;
_ -> _ ->
Action1 = Action#{stream_keys_to_wait => StreamKeysToWait1, progresses => Progresses1}, Action1 = Action#{stream_keys_to_wait => StreamKeysToWait1, progresses => Progresses1},
?tp(warning, shared_subs_schedule_action_continue, #{ ?tp(warning, shared_subs_schedule_action_continue, #{
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
new_action => format_schedule_action(Action1) new_action => format_schedule_action(Action1)
}), }),
{continue, Action1} {continue, Action1}
@ -551,8 +550,8 @@ on_disconnect(S0, #{agent := Agent0} = SharedSubS0) ->
revoke_all_streams(S0) -> revoke_all_streams(S0) ->
fold_shared_stream_states( fold_shared_stream_states(
fun(TopicFilter, Stream, _SRS, S) -> fun(ShareTopicFilter, Stream, _SRS, S) ->
revoke_stream(#{topic_filter => TopicFilter, stream => Stream}, S) revoke_stream(#{share_topic_filter => ShareTopicFilter, stream => Stream}, S)
end, end,
S0, S0,
S0 S0
@ -580,8 +579,8 @@ to_map(_S, _SharedSubS) ->
%% Generic helpers %% Generic helpers
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
lookup(TopicFilter, S) -> lookup(ShareTopicFilter, S) ->
case emqx_persistent_session_ds_state:get_subscription(TopicFilter, S) of case emqx_persistent_session_ds_state:get_subscription(ShareTopicFilter, S) of
Sub = #{current_state := SStateId} -> Sub = #{current_state := SStateId} ->
case emqx_persistent_session_ds_state:get_subscription_state(SStateId, S) of case emqx_persistent_session_ds_state:get_subscription_state(SStateId, S) of
#{subopts := SubOpts} -> #{subopts := SubOpts} ->
@ -640,7 +639,7 @@ stream_progress(
fold_shared_subs(Fun, Acc, S) -> fold_shared_subs(Fun, Acc, S) ->
emqx_persistent_session_ds_state:fold_subscriptions( emqx_persistent_session_ds_state:fold_subscriptions(
fun fun
(#share{} = TopicFilter, Sub, Acc0) -> Fun(TopicFilter, Sub, Acc0); (#share{} = ShareTopicFilter, Sub, Acc0) -> Fun(ShareTopicFilter, Sub, Acc0);
(_, _Sub, Acc0) -> Acc0 (_, _Sub, Acc0) -> Acc0
end, end,
Acc, Acc,
@ -650,10 +649,10 @@ fold_shared_subs(Fun, Acc, S) ->
fold_shared_stream_states(Fun, Acc, S) -> fold_shared_stream_states(Fun, Acc, S) ->
%% TODO %% TODO
%% Optimize or cache %% Optimize or cache
TopicFilters = fold_shared_subs( ShareTopicFilters = fold_shared_subs(
fun fun
(#share{} = TopicFilter, #{id := Id} = _Sub, Acc0) -> (#share{} = ShareTopicFilter, #{id := Id} = _Sub, Acc0) ->
Acc0#{Id => TopicFilter}; Acc0#{Id => ShareTopicFilter};
(_, _, Acc0) -> (_, _, Acc0) ->
Acc0 Acc0
end, end,
@ -662,9 +661,9 @@ fold_shared_stream_states(Fun, Acc, S) ->
), ),
emqx_persistent_session_ds_state:fold_streams( emqx_persistent_session_ds_state:fold_streams(
fun({SubId, Stream}, SRS, Acc0) -> fun({SubId, Stream}, SRS, Acc0) ->
case TopicFilters of case ShareTopicFilters of
#{SubId := TopicFilter} -> #{SubId := ShareTopicFilter} ->
Fun(TopicFilter, Stream, SRS, Acc0); Fun(ShareTopicFilter, Stream, SRS, Acc0);
_ -> _ ->
Acc0 Acc0
end end

View File

@ -15,7 +15,7 @@
}. }.
-type t() :: term(). -type t() :: term().
-type topic_filter() :: emqx_persistent_session_ds:share_topic_filter(). -type share_topic_filter() :: emqx_persistent_session_ds:share_topic_filter().
-type opts() :: #{ -type opts() :: #{
session_id := session_id() session_id := session_id()
@ -28,21 +28,21 @@
-type stream_lease() :: #{ -type stream_lease() :: #{
type => lease, type => lease,
%% Used as "external" subscription_id %% Used as "external" subscription_id
topic_filter := topic_filter(), share_topic_filter := share_topic_filter(),
stream := emqx_ds:stream(), stream := emqx_ds:stream(),
iterator := emqx_ds:iterator() iterator := emqx_ds:iterator()
}. }.
-type stream_revoke() :: #{ -type stream_revoke() :: #{
type => revoke, type => revoke,
topic_filter := topic_filter(), share_topic_filter := share_topic_filter(),
stream := emqx_ds:stream() stream := emqx_ds:stream()
}. }.
-type stream_lease_event() :: stream_lease() | stream_revoke(). -type stream_lease_event() :: stream_lease() | stream_revoke().
-type stream_progress() :: #{ -type stream_progress() :: #{
topic_filter := topic_filter(), share_topic_filter := share_topic_filter(),
stream := emqx_ds:stream(), stream := emqx_ds:stream(),
iterator := emqx_ds:iterator(), iterator := emqx_ds:iterator(),
use_finished := boolean() use_finished := boolean()
@ -80,13 +80,13 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-callback new(opts()) -> t(). -callback new(opts()) -> t().
-callback open([{topic_filter(), subscription()}], opts()) -> t(). -callback open([{share_topic_filter(), subscription()}], opts()) -> t().
-callback can_subscribe(t(), topic_filter(), emqx_types:subopts()) -> ok | {error, term()}. -callback can_subscribe(t(), share_topic_filter(), emqx_types:subopts()) -> ok | {error, term()}.
-callback on_subscribe(t(), topic_filter(), emqx_types:subopts()) -> t(). -callback on_subscribe(t(), share_topic_filter(), emqx_types:subopts()) -> t().
-callback on_unsubscribe(t(), topic_filter(), [stream_progress()]) -> t(). -callback on_unsubscribe(t(), share_topic_filter(), [stream_progress()]) -> t().
-callback on_disconnect(t(), #{emqx_types:group() => [stream_progress()]}) -> t(). -callback on_disconnect(t(), #{share_topic_filter() => [stream_progress()]}) -> t().
-callback renew_streams(t()) -> {[stream_lease_event()], t()}. -callback renew_streams(t()) -> {[stream_lease_event()], t()}.
-callback on_stream_progress(t(), #{emqx_types:group() => [stream_progress()]}) -> t(). -callback on_stream_progress(t(), #{share_topic_filter() => [stream_progress()]}) -> t().
-callback on_info(t(), term()) -> t(). -callback on_info(t(), term()) -> t().
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -97,23 +97,23 @@
new(Opts) -> new(Opts) ->
?shared_subs_agent:new(Opts). ?shared_subs_agent:new(Opts).
-spec open([{topic_filter(), subscription()}], opts()) -> t(). -spec open([{share_topic_filter(), subscription()}], opts()) -> t().
open(Topics, Opts) -> open(Topics, Opts) ->
?shared_subs_agent:open(Topics, Opts). ?shared_subs_agent:open(Topics, Opts).
-spec can_subscribe(t(), topic_filter(), emqx_types:subopts()) -> ok | {error, term()}. -spec can_subscribe(t(), share_topic_filter(), emqx_types:subopts()) -> ok | {error, term()}.
can_subscribe(Agent, TopicFilter, SubOpts) -> can_subscribe(Agent, ShareTopicFilter, SubOpts) ->
?shared_subs_agent:can_subscribe(Agent, TopicFilter, SubOpts). ?shared_subs_agent:can_subscribe(Agent, ShareTopicFilter, SubOpts).
-spec on_subscribe(t(), topic_filter(), emqx_types:subopts()) -> t(). -spec on_subscribe(t(), share_topic_filter(), emqx_types:subopts()) -> t().
on_subscribe(Agent, TopicFilter, SubOpts) -> on_subscribe(Agent, ShareTopicFilter, SubOpts) ->
?shared_subs_agent:on_subscribe(Agent, TopicFilter, SubOpts). ?shared_subs_agent:on_subscribe(Agent, ShareTopicFilter, SubOpts).
-spec on_unsubscribe(t(), topic_filter(), [stream_progress()]) -> t(). -spec on_unsubscribe(t(), share_topic_filter(), [stream_progress()]) -> t().
on_unsubscribe(Agent, TopicFilter, StreamProgresses) -> on_unsubscribe(Agent, ShareTopicFilter, StreamProgresses) ->
?shared_subs_agent:on_unsubscribe(Agent, TopicFilter, StreamProgresses). ?shared_subs_agent:on_unsubscribe(Agent, ShareTopicFilter, StreamProgresses).
-spec on_disconnect(t(), #{emqx_types:group() => [stream_progress()]}) -> t(). -spec on_disconnect(t(), #{share_topic_filter() => [stream_progress()]}) -> t().
on_disconnect(Agent, StreamProgresses) -> on_disconnect(Agent, StreamProgresses) ->
?shared_subs_agent:on_disconnect(Agent, StreamProgresses). ?shared_subs_agent:on_disconnect(Agent, StreamProgresses).
@ -121,7 +121,7 @@ on_disconnect(Agent, StreamProgresses) ->
renew_streams(Agent) -> renew_streams(Agent) ->
?shared_subs_agent:renew_streams(Agent). ?shared_subs_agent:renew_streams(Agent).
-spec on_stream_progress(t(), #{emqx_types:group() => [stream_progress()]}) -> t(). -spec on_stream_progress(t(), #{share_topic_filter() => [stream_progress()]}) -> t().
on_stream_progress(Agent, StreamProgress) -> on_stream_progress(Agent, StreamProgress) ->
?shared_subs_agent:on_stream_progress(Agent, StreamProgress). ?shared_subs_agent:on_stream_progress(Agent, StreamProgress).

View File

@ -26,18 +26,66 @@
-behaviour(emqx_persistent_session_ds_shared_subs_agent). -behaviour(emqx_persistent_session_ds_shared_subs_agent).
-type share_topic_filter() :: emqx_persistent_session_ds:share_topic_filter().
-type group_id() :: share_topic_filter().
-type progress() :: emqx_persistent_session_ds_shared_subs:progress().
-type external_lease_event() ::
#{
type => lease,
stream => emqx_ds:stream(),
progress => progress(),
share_topic_filter => emqx_persistent_session_ds:share_topic_filter()
}
| #{
type => revoke,
stream => emqx_ds:stream(),
share_topic_filter => emqx_persistent_session_ds:share_topic_filter()
}.
-type options() :: #{
session_id := emqx_persistent_session_ds:id()
}.
-type t() :: #{
groups := #{
group_id() => emqx_ds_shared_sub_group_sm:t()
},
session_id := emqx_persistent_session_ds:id()
}.
%% Techinically, group_id and share_topic_filter are the same.
%% However, we speak in the terms of share_topic_filter in the API,
%% which is known to the shared subscription handler of persistent session.
%%
%% And we speak in the terms of group_id internally:
%% * we keep group_sm's in the state by group_id
%% * we use group_id to address group_sm's, e.g. when sending messages to them
%% from leader or from themselves.
-define(group_id(ShareTopicFilter), ShareTopicFilter).
-define(share_topic_filter(GroupId), GroupId).
-record(message_to_group_sm, { -record(message_to_group_sm, {
group :: emqx_types:group(), group_id :: group_id(),
message :: term() message :: term()
}). }).
-export_type([
t/0,
group_id/0,
options/0,
external_lease_event/0
]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% API %% API
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-spec new(options()) -> t().
new(Opts) -> new(Opts) ->
init_state(Opts). init_state(Opts).
-spec open([{share_topic_filter(), emqx_types:subopts()}], options()) -> t().
open(TopicSubscriptions, Opts) -> open(TopicSubscriptions, Opts) ->
State0 = init_state(Opts), State0 = init_state(Opts),
State1 = lists:foldl( State1 = lists:foldl(
@ -45,32 +93,41 @@ open(TopicSubscriptions, Opts) ->
?tp(warning, ds_agent_open_subscription, #{ ?tp(warning, ds_agent_open_subscription, #{
topic_filter => ShareTopicFilter topic_filter => ShareTopicFilter
}), }),
add_group_subscription(State, ShareTopicFilter) add_shared_subscription(State, ShareTopicFilter)
end, end,
State0, State0,
TopicSubscriptions TopicSubscriptions
), ),
State1. State1.
can_subscribe(_State, _TopicFilter, _SubOpts) -> -spec can_subscribe(t(), share_topic_filter(), emqx_types:subopts()) -> ok.
can_subscribe(_State, _ShareTopicFilter, _SubOpts) ->
ok. ok.
on_subscribe(State0, TopicFilter, _SubOpts) -> -spec on_subscribe(t(), share_topic_filter(), emqx_types:subopts()) -> t().
on_subscribe(State0, ShareTopicFilter, _SubOpts) ->
?tp(warning, ds_agent_on_subscribe, #{ ?tp(warning, ds_agent_on_subscribe, #{
topic_filter => TopicFilter share_topic_filter => ShareTopicFilter
}), }),
add_group_subscription(State0, TopicFilter). add_shared_subscription(State0, ShareTopicFilter).
on_unsubscribe(State, TopicFilter, GroupProgress) -> -spec on_unsubscribe(t(), share_topic_filter(), [
delete_group_subscription(State, TopicFilter, GroupProgress). emqx_persistent_session_ds_shared_subs:agent_stream_progress()
]) -> t().
on_unsubscribe(State, ShareTopicFilter, GroupProgress) ->
delete_shared_subscription(State, ShareTopicFilter, GroupProgress).
-spec renew_streams(t()) -> {[emqx_persistent_session_ds_shared_subs:agent_stream_event()], t()}.
renew_streams(#{} = State) -> renew_streams(#{} = State) ->
fetch_stream_events(State). fetch_stream_events(State).
-spec on_stream_progress(t(), #{
share_topic_filter() => [emqx_persistent_session_ds_shared_subs:agent_stream_progress()]
}) -> t().
on_stream_progress(State, StreamProgresses) -> on_stream_progress(State, StreamProgresses) ->
maps:fold( maps:fold(
fun(Group, GroupProgresses, StateAcc) -> fun(ShareTopicFilter, GroupProgresses, StateAcc) ->
with_group_sm(StateAcc, Group, fun(GSM) -> with_group_sm(StateAcc, ?group_id(ShareTopicFilter), fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_stream_progress(GSM, GroupProgresses) emqx_ds_shared_sub_group_sm:handle_stream_progress(GSM, GroupProgresses)
end) end)
end, end,
@ -78,72 +135,74 @@ on_stream_progress(State, StreamProgresses) ->
StreamProgresses StreamProgresses
). ).
-spec on_disconnect(t(), [emqx_persistent_session_ds_shared_subs:agent_stream_progress()]) -> t().
on_disconnect(#{groups := Groups0} = State, StreamProgresses) -> on_disconnect(#{groups := Groups0} = State, StreamProgresses) ->
ok = maps:foreach( ok = maps:foreach(
fun(Group, GroupSM0) -> fun(GroupId, GroupSM0) ->
GroupProgresses = maps:get(Group, StreamProgresses, []), GroupProgresses = maps:get(?share_topic_filter(GroupId), StreamProgresses, []),
emqx_ds_shared_sub_group_sm:handle_disconnect(GroupSM0, GroupProgresses) emqx_ds_shared_sub_group_sm:handle_disconnect(GroupSM0, GroupProgresses)
end, end,
Groups0 Groups0
), ),
State#{groups => #{}}. State#{groups => #{}}.
on_info(State, ?leader_lease_streams_match(Group, Leader, StreamProgresses, Version)) -> -spec on_info(t(), term()) -> t().
on_info(State, ?leader_lease_streams_match(GroupId, Leader, StreamProgresses, Version)) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => leader_lease_streams, msg => leader_lease_streams,
group => Group, group_id => GroupId,
streams => StreamProgresses, streams => StreamProgresses,
version => Version, version => Version,
leader => Leader leader => Leader
}), }),
with_group_sm(State, Group, fun(GSM) -> with_group_sm(State, GroupId, fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_leader_lease_streams( emqx_ds_shared_sub_group_sm:handle_leader_lease_streams(
GSM, Leader, StreamProgresses, Version GSM, Leader, StreamProgresses, Version
) )
end); end);
on_info(State, ?leader_renew_stream_lease_match(Group, Version)) -> on_info(State, ?leader_renew_stream_lease_match(GroupId, Version)) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => leader_renew_stream_lease, msg => leader_renew_stream_lease,
group => Group, group_id => GroupId,
version => Version version => Version
}), }),
with_group_sm(State, Group, fun(GSM) -> with_group_sm(State, GroupId, fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_leader_renew_stream_lease(GSM, Version) emqx_ds_shared_sub_group_sm:handle_leader_renew_stream_lease(GSM, Version)
end); end);
on_info(State, ?leader_renew_stream_lease_match(Group, VersionOld, VersionNew)) -> on_info(State, ?leader_renew_stream_lease_match(GroupId, VersionOld, VersionNew)) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => leader_renew_stream_lease, msg => leader_renew_stream_lease,
group => Group, group_id => GroupId,
version_old => VersionOld, version_old => VersionOld,
version_new => VersionNew version_new => VersionNew
}), }),
with_group_sm(State, Group, fun(GSM) -> with_group_sm(State, GroupId, fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_leader_renew_stream_lease(GSM, VersionOld, VersionNew) emqx_ds_shared_sub_group_sm:handle_leader_renew_stream_lease(GSM, VersionOld, VersionNew)
end); end);
on_info(State, ?leader_update_streams_match(Group, VersionOld, VersionNew, StreamsNew)) -> on_info(State, ?leader_update_streams_match(GroupId, VersionOld, VersionNew, StreamsNew)) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => leader_update_streams, msg => leader_update_streams,
group => Group, group_id => GroupId,
version_old => VersionOld, version_old => VersionOld,
version_new => VersionNew, version_new => VersionNew,
streams_new => StreamsNew streams_new => StreamsNew
}), }),
with_group_sm(State, Group, fun(GSM) -> with_group_sm(State, GroupId, fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_leader_update_streams( emqx_ds_shared_sub_group_sm:handle_leader_update_streams(
GSM, VersionOld, VersionNew, StreamsNew GSM, VersionOld, VersionNew, StreamsNew
) )
end); end);
on_info(State, ?leader_invalidate_match(Group)) -> on_info(State, ?leader_invalidate_match(GroupId)) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => leader_invalidate, msg => leader_invalidate,
group => Group group_id => GroupId
}), }),
with_group_sm(State, Group, fun(GSM) -> with_group_sm(State, GroupId, fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_leader_invalidate(GSM) emqx_ds_shared_sub_group_sm:handle_leader_invalidate(GSM)
end); end);
%% Generic messages sent by group_sm's to themselves (timeouts). %% Generic messages sent by group_sm's to themselves (timeouts).
on_info(State, #message_to_group_sm{group = Group, message = Message}) -> on_info(State, #message_to_group_sm{group_id = GroupId, message = Message}) ->
with_group_sm(State, Group, fun(GSM) -> with_group_sm(State, GroupId, fun(GSM) ->
emqx_ds_shared_sub_group_sm:handle_info(GSM, Message) emqx_ds_shared_sub_group_sm:handle_info(GSM, Message)
end). end).
@ -158,29 +217,30 @@ init_state(Opts) ->
groups => #{} groups => #{}
}. }.
delete_group_subscription(State, #share{group = Group}, GroupProgress) -> delete_shared_subscription(State, ShareTopicFilter, GroupProgress) ->
GroupId = ?group_id(ShareTopicFilter),
case State of case State of
#{groups := #{Group := GSM} = Groups} -> #{groups := #{GroupId := GSM} = Groups} ->
_ = emqx_ds_shared_sub_group_sm:handle_disconnect(GSM, GroupProgress), _ = emqx_ds_shared_sub_group_sm:handle_disconnect(GSM, GroupProgress),
State#{groups => maps:remove(Group, Groups)}; State#{groups => maps:remove(GroupId, Groups)};
_ -> _ ->
State State
end. end.
add_group_subscription( add_shared_subscription(
#{session_id := SessionId, groups := Groups0} = State0, ShareTopicFilter #{session_id := SessionId, groups := Groups0} = State0, ShareTopicFilter
) -> ) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => agent_add_group_subscription, msg => agent_add_shared_subscription,
topic_filter => ShareTopicFilter share_topic_filter => ShareTopicFilter
}), }),
#share{group = Group} = ShareTopicFilter, GroupId = ?group_id(ShareTopicFilter),
Groups1 = Groups0#{ Groups1 = Groups0#{
Group => emqx_ds_shared_sub_group_sm:new(#{ GroupId => emqx_ds_shared_sub_group_sm:new(#{
session_id => SessionId, session_id => SessionId,
topic_filter => ShareTopicFilter, share_topic_filter => ShareTopicFilter,
agent => this_agent(SessionId), agent => this_agent(SessionId),
send_after => send_to_subscription_after(Group) send_after => send_to_subscription_after(GroupId)
}) })
}, },
State1 = State0#{groups => Groups1}, State1 = State0#{groups => Groups1},
@ -188,9 +248,9 @@ add_group_subscription(
fetch_stream_events(#{groups := Groups0} = State0) -> fetch_stream_events(#{groups := Groups0} = State0) ->
{Groups1, Events} = maps:fold( {Groups1, Events} = maps:fold(
fun(Group, GroupSM0, {GroupsAcc, EventsAcc}) -> fun(GroupId, GroupSM0, {GroupsAcc, EventsAcc}) ->
{GroupSM1, Events} = emqx_ds_shared_sub_group_sm:fetch_stream_events(GroupSM0), {GroupSM1, Events} = emqx_ds_shared_sub_group_sm:fetch_stream_events(GroupSM0),
{GroupsAcc#{Group => GroupSM1}, [Events | EventsAcc]} {GroupsAcc#{GroupId => GroupSM1}, [Events | EventsAcc]}
end, end,
{#{}, []}, {#{}, []},
Groups0 Groups0
@ -201,20 +261,20 @@ fetch_stream_events(#{groups := Groups0} = State0) ->
this_agent(Id) -> this_agent(Id) ->
emqx_ds_shared_sub_proto:agent(Id, self()). emqx_ds_shared_sub_proto:agent(Id, self()).
send_to_subscription_after(Group) -> send_to_subscription_after(GroupId) ->
fun(Time, Msg) -> fun(Time, Msg) ->
emqx_persistent_session_ds_shared_subs_agent:send_after( emqx_persistent_session_ds_shared_subs_agent:send_after(
Time, Time,
self(), self(),
#message_to_group_sm{group = Group, message = Msg} #message_to_group_sm{group_id = GroupId, message = Msg}
) )
end. end.
with_group_sm(State, Group, Fun) -> with_group_sm(State, GroupId, Fun) ->
case State of case State of
#{groups := #{Group := GSM0} = Groups} -> #{groups := #{GroupId := GSM0} = Groups} ->
#{} = GSM1 = Fun(GSM0), #{} = GSM1 = Fun(GSM0),
State#{groups => Groups#{Group => GSM1}}; State#{groups => Groups#{GroupId => GSM1}};
_ -> _ ->
%% TODO %% TODO
%% Error? %% Error?

View File

@ -41,7 +41,7 @@
-type options() :: #{ -type options() :: #{
session_id := emqx_persistent_session_ds:id(), session_id := emqx_persistent_session_ds:id(),
agent := emqx_ds_shared_sub_proto:agent(), agent := emqx_ds_shared_sub_proto:agent(),
topic_filter := emqx_persistent_session_ds:share_topic_filter(), share_topic_filter := emqx_persistent_session_ds:share_topic_filter(),
send_after := fun((non_neg_integer(), term()) -> reference()) send_after := fun((non_neg_integer(), term()) -> reference())
}. }.
@ -58,19 +58,6 @@
stream => emqx_ds:stream() stream => emqx_ds:stream()
}. }.
-type external_lease_event() ::
#{
type => lease,
stream => emqx_ds:stream(),
progress => progress(),
topic_filter => emqx_persistent_session_ds:share_topic_filter()
}
| #{
type => revoke,
stream => emqx_ds:stream(),
topic_filter => emqx_persistent_session_ds:share_topic_filter()
}.
%% GroupSM States %% GroupSM States
-define(connecting, connecting). -define(connecting, connecting).
@ -111,7 +98,7 @@
-type timer() :: #timer{}. -type timer() :: #timer{}.
-type group_sm() :: #{ -type group_sm() :: #{
topic_filter => emqx_persistent_session_ds:share_topic_filter(), share_topic_filter => emqx_persistent_session_ds:share_topic_filter(),
agent => emqx_ds_shared_sub_proto:agent(), agent => emqx_ds_shared_sub_proto:agent(),
send_after => fun((non_neg_integer(), term()) -> reference()), send_after => fun((non_neg_integer(), term()) -> reference()),
stream_lease_events => list(stream_lease_event()), stream_lease_events => list(stream_lease_event()),
@ -129,7 +116,7 @@
new(#{ new(#{
session_id := SessionId, session_id := SessionId,
agent := Agent, agent := Agent,
topic_filter := ShareTopicFilter, share_topic_filter := ShareTopicFilter,
send_after := SendAfter send_after := SendAfter
}) -> }) ->
?SLOG( ?SLOG(
@ -137,32 +124,33 @@ new(#{
#{ #{
msg => group_sm_new, msg => group_sm_new,
agent => Agent, agent => Agent,
topic_filter => ShareTopicFilter share_topic_filter => ShareTopicFilter
} }
), ),
GSM0 = #{ GSM0 = #{
id => SessionId, id => SessionId,
topic_filter => ShareTopicFilter, share_topic_filter => ShareTopicFilter,
agent => Agent, agent => Agent,
send_after => SendAfter send_after => SendAfter
}, },
?tp(warning, group_sm_new, #{ ?tp(warning, group_sm_new, #{
agent => Agent, agent => Agent,
topic_filter => ShareTopicFilter share_topic_filter => ShareTopicFilter
}), }),
transition(GSM0, ?connecting, #{}). transition(GSM0, ?connecting, #{}).
-spec fetch_stream_events(group_sm()) -> {group_sm(), list(external_lease_event())}. -spec fetch_stream_events(group_sm()) ->
{group_sm(), [emqx_ds_shared_sub_agent:external_lease_event()]}.
fetch_stream_events( fetch_stream_events(
#{ #{
state := _State, state := _State,
topic_filter := TopicFilter, share_topic_filter := ShareTopicFilter,
stream_lease_events := Events0 stream_lease_events := Events0
} = GSM } = GSM
) -> ) ->
Events1 = lists:map( Events1 = lists:map(
fun(Event) -> fun(Event) ->
Event#{topic_filter => TopicFilter} Event#{share_topic_filter => ShareTopicFilter}
end, end,
Events0 Events0
), ),
@ -187,18 +175,21 @@ handle_disconnect(
%%----------------------------------------------------------------------- %%-----------------------------------------------------------------------
%% Connecting state %% Connecting state
handle_connecting(#{agent := Agent, topic_filter := ShareTopicFilter} = GSM) -> handle_connecting(#{agent := Agent, share_topic_filter := ShareTopicFilter} = GSM) ->
?tp(warning, group_sm_enter_connecting, #{ ?tp(warning, group_sm_enter_connecting, #{
agent => Agent, agent => Agent,
topic_filter => ShareTopicFilter share_topic_filter => ShareTopicFilter
}), }),
ok = emqx_ds_shared_sub_registry:lookup_leader(Agent, agent_metadata(GSM), ShareTopicFilter), ok = emqx_ds_shared_sub_registry:lookup_leader(Agent, agent_metadata(GSM), ShareTopicFilter),
ensure_state_timeout(GSM, find_leader_timeout, ?dq_config(session_find_leader_timeout_ms)). ensure_state_timeout(GSM, find_leader_timeout, ?dq_config(session_find_leader_timeout_ms)).
handle_leader_lease_streams( handle_leader_lease_streams(
#{state := ?connecting, topic_filter := TopicFilter} = GSM0, Leader, StreamProgresses, Version #{state := ?connecting, share_topic_filter := ShareTopicFilter} = GSM0,
Leader,
StreamProgresses,
Version
) -> ) ->
?tp(debug, leader_lease_streams, #{topic_filter => TopicFilter}), ?tp(debug, leader_lease_streams, #{share_topic_filter => ShareTopicFilter}),
Streams = progresses_to_map(StreamProgresses), Streams = progresses_to_map(StreamProgresses),
StreamLeaseEvents = progresses_to_lease_events(StreamProgresses), StreamLeaseEvents = progresses_to_lease_events(StreamProgresses),
transition( transition(
@ -215,12 +206,12 @@ handle_leader_lease_streams(
handle_leader_lease_streams(GSM, _Leader, _StreamProgresses, _Version) -> handle_leader_lease_streams(GSM, _Leader, _StreamProgresses, _Version) ->
GSM. GSM.
handle_find_leader_timeout(#{agent := Agent, topic_filter := TopicFilter} = GSM0) -> handle_find_leader_timeout(#{agent := Agent, share_topic_filter := ShareTopicFilter} = GSM0) ->
?tp(warning, group_sm_find_leader_timeout, #{ ?tp(warning, group_sm_find_leader_timeout, #{
agent => Agent, agent => Agent,
topic_filter => TopicFilter share_topic_filter => ShareTopicFilter
}), }),
ok = emqx_ds_shared_sub_registry:lookup_leader(Agent, agent_metadata(GSM0), TopicFilter), ok = emqx_ds_shared_sub_registry:lookup_leader(Agent, agent_metadata(GSM0), ShareTopicFilter),
GSM1 = ensure_state_timeout( GSM1 = ensure_state_timeout(
GSM0, find_leader_timeout, ?dq_config(session_find_leader_timeout_ms) GSM0, find_leader_timeout, ?dq_config(session_find_leader_timeout_ms)
), ),
@ -238,8 +229,8 @@ handle_replaying(GSM0) ->
), ),
GSM2. GSM2.
handle_renew_lease_timeout(#{agent := Agent, topic_filter := TopicFilter} = GSM) -> handle_renew_lease_timeout(#{agent := Agent, share_topic_filter := ShareTopicFilter} = GSM) ->
?tp(warning, renew_lease_timeout, #{agent => Agent, topic_filter => TopicFilter}), ?tp(warning, renew_lease_timeout, #{agent => Agent, share_topic_filter => ShareTopicFilter}),
transition(GSM, ?connecting, #{}). transition(GSM, ?connecting, #{}).
%%----------------------------------------------------------------------- %%-----------------------------------------------------------------------
@ -429,10 +420,10 @@ handle_stream_progress(
handle_stream_progress(#{state := ?disconnected} = GSM, _StreamProgresses) -> handle_stream_progress(#{state := ?disconnected} = GSM, _StreamProgresses) ->
GSM. GSM.
handle_leader_invalidate(#{agent := Agent, topic_filter := TopicFilter} = GSM) -> handle_leader_invalidate(#{agent := Agent, share_topic_filter := ShareTopicFilter} = GSM) ->
?tp(warning, shared_sub_group_sm_leader_invalidate, #{ ?tp(warning, shared_sub_group_sm_leader_invalidate, #{
agent => Agent, agent => Agent,
topic_filter => TopicFilter share_topic_filter => ShareTopicFilter
}), }),
transition(GSM, ?connecting, #{}). transition(GSM, ?connecting, #{}).
@ -441,11 +432,11 @@ handle_leader_invalidate(#{agent := Agent, topic_filter := TopicFilter} = GSM) -
%%----------------------------------------------------------------------- %%-----------------------------------------------------------------------
handle_state_timeout( handle_state_timeout(
#{state := ?connecting, topic_filter := TopicFilter} = GSM, #{state := ?connecting, share_topic_filter := ShareTopicFilter} = GSM,
find_leader_timeout, find_leader_timeout,
_Message _Message
) -> ) ->
?tp(debug, find_leader_timeout, #{topic_filter => TopicFilter}), ?tp(debug, find_leader_timeout, #{share_topic_filter => ShareTopicFilter}),
handle_find_leader_timeout(GSM); handle_find_leader_timeout(GSM);
handle_state_timeout( handle_state_timeout(
#{state := ?replaying} = GSM, #{state := ?replaying} = GSM,

View File

@ -27,8 +27,12 @@
terminate/3 terminate/3
]). ]).
-type share_topic_filter() :: emqx_persistent_session_ds:share_topic_filter().
-type group_id() :: share_topic_filter().
-type options() :: #{ -type options() :: #{
topic_filter := emqx_persistent_session_ds:share_topic_filter() share_topic_filter := share_topic_filter()
}. }.
%% Agent states %% Agent states
@ -39,7 +43,7 @@
-define(updating, updating). -define(updating, updating).
-type agent_state() :: #{ -type agent_state() :: #{
%% Our view of group sm's status %% Our view of group_id sm's status
%% it lags the actual state %% it lags the actual state
state := ?waiting_replaying | ?replaying | ?waiting_updating | ?updating, state := ?waiting_replaying | ?replaying | ?waiting_updating | ?updating,
prev_version := emqx_maybe:t(emqx_ds_shared_sub_proto:version()), prev_version := emqx_maybe:t(emqx_ds_shared_sub_proto:version()),
@ -62,7 +66,7 @@
%% %%
%% Persistent data %% Persistent data
%% %%
group := emqx_types:group(), group_id := group_id(),
topic := emqx_types:topic(), topic := emqx_types:topic(),
%% For ds router, not an actual session_id %% For ds router, not an actual session_id
router_id := binary(), router_id := binary(),
@ -119,9 +123,9 @@ register(Pid, Fun) ->
%% Internal API %% Internal API
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
child_spec(#{topic_filter := TopicFilter} = Options) -> child_spec(#{share_topic_filter := ShareTopicFilter} = Options) ->
#{ #{
id => id(TopicFilter), id => id(ShareTopicFilter),
start => {?MODULE, start_link, [Options]}, start => {?MODULE, start_link, [Options]},
restart => temporary, restart => temporary,
shutdown => 5000, shutdown => 5000,
@ -131,8 +135,8 @@ child_spec(#{topic_filter := TopicFilter} = Options) ->
start_link(Options) -> start_link(Options) ->
gen_statem:start_link(?MODULE, [Options], []). gen_statem:start_link(?MODULE, [Options], []).
id(#share{group = Group} = _TopicFilter) -> id(ShareTopicFilter) ->
{?MODULE, Group}. {?MODULE, ShareTopicFilter}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% gen_statem callbacks %% gen_statem callbacks
@ -140,9 +144,9 @@ id(#share{group = Group} = _TopicFilter) ->
callback_mode() -> [handle_event_function, state_enter]. callback_mode() -> [handle_event_function, state_enter].
init([#{topic_filter := #share{group = Group, topic = Topic}} = _Options]) -> init([#{share_topic_filter := #share{topic = Topic} = ShareTopicFilter} = _Options]) ->
Data = #{ Data = #{
group => Group, group_id => ShareTopicFilter,
topic => Topic, topic => Topic,
router_id => gen_router_id(), router_id => gen_router_id(),
start_time => now_ms() - ?START_TIME_THRESHOLD, start_time => now_ms() - ?START_TIME_THRESHOLD,
@ -463,14 +467,14 @@ select_streams_for_assign(Data0, _Agent, AssignCount) ->
%% Handle a newly connected agent %% Handle a newly connected agent
connect_agent( connect_agent(
#{group := Group, agents := Agents} = Data, #{group_id := GroupId, agents := Agents} = Data,
Agent, Agent,
AgentMetadata AgentMetadata
) -> ) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => leader_agent_connected, msg => leader_agent_connected,
agent => Agent, agent => Agent,
group => Group group_id => GroupId
}), }),
case Agents of case Agents of
#{Agent := AgentState} -> #{Agent := AgentState} ->
@ -583,22 +587,22 @@ renew_leases(#{agents := AgentStates} = Data) ->
), ),
Data. Data.
renew_lease(#{group := Group}, Agent, #{state := ?replaying, version := Version}) -> renew_lease(#{group_id := GroupId}, Agent, #{state := ?replaying, version := Version}) ->
ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, Group, Version); ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, GroupId, Version);
renew_lease(#{group := Group}, Agent, #{state := ?waiting_replaying, version := Version}) -> renew_lease(#{group_id := GroupId}, Agent, #{state := ?waiting_replaying, version := Version}) ->
ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, Group, Version); ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, GroupId, Version);
renew_lease(#{group := Group} = Data, Agent, #{ renew_lease(#{group_id := GroupId} = Data, Agent, #{
streams := Streams, state := ?waiting_updating, version := Version, prev_version := PrevVersion streams := Streams, state := ?waiting_updating, version := Version, prev_version := PrevVersion
}) -> }) ->
StreamProgresses = stream_progresses(Data, Streams), StreamProgresses = stream_progresses(Data, Streams),
ok = emqx_ds_shared_sub_proto:leader_update_streams( ok = emqx_ds_shared_sub_proto:leader_update_streams(
Agent, Group, PrevVersion, Version, StreamProgresses Agent, GroupId, PrevVersion, Version, StreamProgresses
), ),
ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, Group, PrevVersion, Version); ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, GroupId, PrevVersion, Version);
renew_lease(#{group := Group}, Agent, #{ renew_lease(#{group_id := GroupId}, Agent, #{
state := ?updating, version := Version, prev_version := PrevVersion state := ?updating, version := Version, prev_version := PrevVersion
}) -> }) ->
ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, Group, PrevVersion, Version). ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, GroupId, PrevVersion, Version).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Handle stream progress updates from agent in replaying state %% Handle stream progress updates from agent in replaying state
@ -802,7 +806,7 @@ update_agent_stream_states(Data0, Agent, AgentStreamProgresses, VersionOld, Vers
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
agent_transition_to_waiting_updating( agent_transition_to_waiting_updating(
#{group := Group} = Data, #{group_id := GroupId} = Data,
Agent, Agent,
#{state := OldState, version := Version, prev_version := undefined} = AgentState0, #{state := OldState, version := Version, prev_version := undefined} = AgentState0,
Streams, Streams,
@ -825,19 +829,19 @@ agent_transition_to_waiting_updating(
AgentState2 = renew_no_replaying_deadline(AgentState1), AgentState2 = renew_no_replaying_deadline(AgentState1),
StreamProgresses = stream_progresses(Data, Streams), StreamProgresses = stream_progresses(Data, Streams),
ok = emqx_ds_shared_sub_proto:leader_update_streams( ok = emqx_ds_shared_sub_proto:leader_update_streams(
Agent, Group, Version, NewVersion, StreamProgresses Agent, GroupId, Version, NewVersion, StreamProgresses
), ),
AgentState2. AgentState2.
agent_transition_to_waiting_replaying( agent_transition_to_waiting_replaying(
#{group := Group} = _Data, Agent, #{state := OldState, version := Version} = AgentState0 #{group_id := GroupId} = _Data, Agent, #{state := OldState, version := Version} = AgentState0
) -> ) ->
?tp(warning, shared_sub_leader_agent_state_transition, #{ ?tp(warning, shared_sub_leader_agent_state_transition, #{
agent => Agent, agent => Agent,
old_state => OldState, old_state => OldState,
new_state => ?waiting_replaying new_state => ?waiting_replaying
}), }),
ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, Group, Version), ok = emqx_ds_shared_sub_proto:leader_renew_stream_lease(Agent, GroupId, Version),
AgentState1 = AgentState0#{ AgentState1 = AgentState0#{
state => ?waiting_replaying, state => ?waiting_replaying,
revoked_streams => [] revoked_streams => []
@ -845,7 +849,7 @@ agent_transition_to_waiting_replaying(
renew_no_replaying_deadline(AgentState1). renew_no_replaying_deadline(AgentState1).
agent_transition_to_initial_waiting_replaying( agent_transition_to_initial_waiting_replaying(
#{group := Group} = Data, Agent, AgentMetadata, InitialStreams #{group_id := GroupId} = Data, Agent, AgentMetadata, InitialStreams
) -> ) ->
?tp(warning, shared_sub_leader_agent_state_transition, #{ ?tp(warning, shared_sub_leader_agent_state_transition, #{
agent => Agent, agent => Agent,
@ -856,7 +860,7 @@ agent_transition_to_initial_waiting_replaying(
StreamProgresses = stream_progresses(Data, InitialStreams), StreamProgresses = stream_progresses(Data, InitialStreams),
Leader = this_leader(Data), Leader = this_leader(Data),
ok = emqx_ds_shared_sub_proto:leader_lease_streams( ok = emqx_ds_shared_sub_proto:leader_lease_streams(
Agent, Group, Leader, StreamProgresses, Version Agent, GroupId, Leader, StreamProgresses, Version
), ),
AgentState = #{ AgentState = #{
metadata => AgentMetadata, metadata => AgentMetadata,
@ -1015,8 +1019,8 @@ drop_agent(#{agents := Agents} = Data0, Agent) ->
?tp(warning, shared_sub_leader_drop_agent, #{agent => Agent}), ?tp(warning, shared_sub_leader_drop_agent, #{agent => Agent}),
Data1#{agents => maps:remove(Agent, Agents)}. Data1#{agents => maps:remove(Agent, Agents)}.
invalidate_agent(#{group := Group}, Agent) -> invalidate_agent(#{group_id := GroupId}, Agent) ->
ok = emqx_ds_shared_sub_proto:leader_invalidate(Agent, Group). ok = emqx_ds_shared_sub_proto:leader_invalidate(Agent, GroupId).
drop_invalidate_agent(Data0, Agent) -> drop_invalidate_agent(Data0, Agent) ->
Data1 = drop_agent(Data0, Agent), Data1 = drop_agent(Data0, Agent),

View File

@ -33,7 +33,7 @@
-type agent() :: ?agent(emqx_persistent_session_ds:id(), pid()). -type agent() :: ?agent(emqx_persistent_session_ds:id(), pid()).
-type leader() :: pid(). -type leader() :: pid().
-type topic_filter() :: emqx_persistent_session_ds:share_topic_filter(). -type share_topic_filter() :: emqx_persistent_session_ds:share_topic_filter().
-type group() :: emqx_types:group(). -type group() :: emqx_types:group().
-type version() :: non_neg_integer(). -type version() :: non_neg_integer().
-type agent_metadata() :: #{ -type agent_metadata() :: #{
@ -63,8 +63,8 @@
%% agent -> leader messages %% agent -> leader messages
-spec agent_connect_leader(leader(), agent(), agent_metadata(), topic_filter()) -> ok. -spec agent_connect_leader(leader(), agent(), agent_metadata(), share_topic_filter()) -> ok.
agent_connect_leader(ToLeader, FromAgent, AgentMetadata, TopicFilter) when agent_connect_leader(ToLeader, FromAgent, AgentMetadata, ShareTopicFilter) when
?is_local_leader(ToLeader) ?is_local_leader(ToLeader)
-> ->
?tp(warning, shared_sub_proto_msg, #{ ?tp(warning, shared_sub_proto_msg, #{
@ -72,13 +72,13 @@ agent_connect_leader(ToLeader, FromAgent, AgentMetadata, TopicFilter) when
to_leader => ToLeader, to_leader => ToLeader,
from_agent => FromAgent, from_agent => FromAgent,
agent_metadata => AgentMetadata, agent_metadata => AgentMetadata,
topic_filter => TopicFilter share_topic_filter => ShareTopicFilter
}), }),
_ = erlang:send(ToLeader, ?agent_connect_leader(FromAgent, AgentMetadata, TopicFilter)), _ = erlang:send(ToLeader, ?agent_connect_leader(FromAgent, AgentMetadata, ShareTopicFilter)),
ok; ok;
agent_connect_leader(ToLeader, FromAgent, AgentMetadata, TopicFilter) -> agent_connect_leader(ToLeader, FromAgent, AgentMetadata, ShareTopicFilter) ->
emqx_ds_shared_sub_proto_v1:agent_connect_leader( emqx_ds_shared_sub_proto_v1:agent_connect_leader(
?leader_node(ToLeader), ToLeader, FromAgent, AgentMetadata, TopicFilter ?leader_node(ToLeader), ToLeader, FromAgent, AgentMetadata, ShareTopicFilter
). ).
-spec agent_update_stream_states(leader(), agent(), list(agent_stream_progress()), version()) -> ok. -spec agent_update_stream_states(leader(), agent(), list(agent_stream_progress()), version()) -> ok.

View File

@ -21,16 +21,16 @@
%% Agent messages sent to the leader. %% Agent messages sent to the leader.
%% Leader talks to many agents, `agent` field is used to identify the sender. %% Leader talks to many agents, `agent` field is used to identify the sender.
-define(agent_connect_leader(Agent, AgentMetadata, TopicFilter), #{ -define(agent_connect_leader(Agent, AgentMetadata, ShareTopicFilter), #{
type => ?agent_connect_leader_msg, type => ?agent_connect_leader_msg,
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
agent_metadata => AgentMetadata, agent_metadata => AgentMetadata,
agent => Agent agent => Agent
}). }).
-define(agent_connect_leader_match(Agent, AgentMetadata, TopicFilter), #{ -define(agent_connect_leader_match(Agent, AgentMetadata, ShareTopicFilter), #{
type := ?agent_connect_leader_msg, type := ?agent_connect_leader_msg,
topic_filter := TopicFilter, share_topic_filter := ShareTopicFilter,
agent_metadata := AgentMetadata, agent_metadata := AgentMetadata,
agent := Agent agent := Agent
}). }).
@ -81,77 +81,77 @@
%% leader messages, sent from the leader to the agent %% leader messages, sent from the leader to the agent
%% Agent may have several shared subscriptions, so may talk to several leaders %% Agent may have several shared subscriptions, so may talk to several leaders
%% `group` field is used to identify the leader. %% `group_id` field is used to identify the leader.
-define(leader_lease_streams_msg, leader_lease_streams). -define(leader_lease_streams_msg, leader_lease_streams).
-define(leader_renew_stream_lease_msg, leader_renew_stream_lease). -define(leader_renew_stream_lease_msg, leader_renew_stream_lease).
-define(leader_lease_streams(Group, Leader, Streams, Version), #{ -define(leader_lease_streams(GrouId, Leader, Streams, Version), #{
type => ?leader_lease_streams_msg, type => ?leader_lease_streams_msg,
streams => Streams, streams => Streams,
version => Version, version => Version,
leader => Leader, leader => Leader,
group => Group group_id => GrouId
}). }).
-define(leader_lease_streams_match(Group, Leader, Streams, Version), #{ -define(leader_lease_streams_match(GroupId, Leader, Streams, Version), #{
type := ?leader_lease_streams_msg, type := ?leader_lease_streams_msg,
streams := Streams, streams := Streams,
version := Version, version := Version,
leader := Leader, leader := Leader,
group := Group group_id := GroupId
}). }).
-define(leader_renew_stream_lease(Group, Version), #{ -define(leader_renew_stream_lease(GroupId, Version), #{
type => ?leader_renew_stream_lease_msg, type => ?leader_renew_stream_lease_msg,
version => Version, version => Version,
group => Group group_id => GroupId
}). }).
-define(leader_renew_stream_lease_match(Group, Version), #{ -define(leader_renew_stream_lease_match(GroupId, Version), #{
type := ?leader_renew_stream_lease_msg, type := ?leader_renew_stream_lease_msg,
version := Version, version := Version,
group := Group group_id := GroupId
}). }).
-define(leader_renew_stream_lease(Group, VersionOld, VersionNew), #{ -define(leader_renew_stream_lease(GroupId, VersionOld, VersionNew), #{
type => ?leader_renew_stream_lease_msg, type => ?leader_renew_stream_lease_msg,
version_old => VersionOld, version_old => VersionOld,
version_new => VersionNew, version_new => VersionNew,
group => Group group_id => GroupId
}). }).
-define(leader_renew_stream_lease_match(Group, VersionOld, VersionNew), #{ -define(leader_renew_stream_lease_match(GroupId, VersionOld, VersionNew), #{
type := ?leader_renew_stream_lease_msg, type := ?leader_renew_stream_lease_msg,
version_old := VersionOld, version_old := VersionOld,
version_new := VersionNew, version_new := VersionNew,
group := Group group_id := GroupId
}). }).
-define(leader_update_streams(Group, VersionOld, VersionNew, StreamsNew), #{ -define(leader_update_streams(GroupId, VersionOld, VersionNew, StreamsNew), #{
type => leader_update_streams, type => leader_update_streams,
version_old => VersionOld, version_old => VersionOld,
version_new => VersionNew, version_new => VersionNew,
streams_new => StreamsNew, streams_new => StreamsNew,
group => Group group_id => GroupId
}). }).
-define(leader_update_streams_match(Group, VersionOld, VersionNew, StreamsNew), #{ -define(leader_update_streams_match(GroupId, VersionOld, VersionNew, StreamsNew), #{
type := leader_update_streams, type := leader_update_streams,
version_old := VersionOld, version_old := VersionOld,
version_new := VersionNew, version_new := VersionNew,
streams_new := StreamsNew, streams_new := StreamsNew,
group := Group group_id := GroupId
}). }).
-define(leader_invalidate(Group), #{ -define(leader_invalidate(GroupId), #{
type => leader_invalidate, type => leader_invalidate,
group => Group group_id => GroupId
}). }).
-define(leader_invalidate_match(Group), #{ -define(leader_invalidate_match(GroupId), #{
type := leader_invalidate, type := leader_invalidate,
group := Group group_id := GroupId
}). }).
%% Helpers %% Helpers

View File

@ -26,7 +26,7 @@
-record(lookup_leader, { -record(lookup_leader, {
agent :: emqx_ds_shared_sub_proto:agent(), agent :: emqx_ds_shared_sub_proto:agent(),
agent_metadata :: emqx_ds_shared_sub_proto:agent_metadata(), agent_metadata :: emqx_ds_shared_sub_proto:agent_metadata(),
topic_filter :: emqx_persistent_session_ds:share_topic_filter() share_topic_filter :: emqx_persistent_session_ds:share_topic_filter()
}). }).
-define(gproc_id(ID), {n, l, ID}). -define(gproc_id(ID), {n, l, ID}).
@ -40,9 +40,9 @@
emqx_ds_shared_sub_proto:agent_metadata(), emqx_ds_shared_sub_proto:agent_metadata(),
emqx_persistent_session_ds:share_topic_filter() emqx_persistent_session_ds:share_topic_filter()
) -> ok. ) -> ok.
lookup_leader(Agent, AgentMetadata, TopicFilter) -> lookup_leader(Agent, AgentMetadata, ShareTopicFilter) ->
gen_server:cast(?MODULE, #lookup_leader{ gen_server:cast(?MODULE, #lookup_leader{
agent = Agent, agent_metadata = AgentMetadata, topic_filter = TopicFilter agent = Agent, agent_metadata = AgentMetadata, share_topic_filter = ShareTopicFilter
}). }).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -72,9 +72,14 @@ handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}. {reply, {error, unknown_request}, State}.
handle_cast( handle_cast(
#lookup_leader{agent = Agent, agent_metadata = AgentMetadata, topic_filter = TopicFilter}, State #lookup_leader{
agent = Agent,
agent_metadata = AgentMetadata,
share_topic_filter = ShareTopicFilter
},
State
) -> ) ->
State1 = do_lookup_leader(Agent, AgentMetadata, TopicFilter, State), State1 = do_lookup_leader(Agent, AgentMetadata, ShareTopicFilter, State),
{noreply, State1}. {noreply, State1}.
handle_info(_Info, State) -> handle_info(_Info, State) ->
@ -87,15 +92,15 @@ terminate(_Reason, _State) ->
%% Internal functions %% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
do_lookup_leader(Agent, AgentMetadata, TopicFilter, State) -> do_lookup_leader(Agent, AgentMetadata, ShareTopicFilter, State) ->
%% TODO https://emqx.atlassian.net/browse/EMQX-12309 %% TODO https://emqx.atlassian.net/browse/EMQX-12309
%% Cluster-wide unique leader election should be implemented %% Cluster-wide unique leader election should be implemented
Id = emqx_ds_shared_sub_leader:id(TopicFilter), Id = emqx_ds_shared_sub_leader:id(ShareTopicFilter),
LeaderPid = LeaderPid =
case gproc:where(?gproc_id(Id)) of case gproc:where(?gproc_id(Id)) of
undefined -> undefined ->
{ok, Pid} = emqx_ds_shared_sub_leader_sup:start_leader(#{ {ok, Pid} = emqx_ds_shared_sub_leader_sup:start_leader(#{
topic_filter => TopicFilter share_topic_filter => ShareTopicFilter
}), }),
{ok, NewLeaderPid} = emqx_ds_shared_sub_leader:register( {ok, NewLeaderPid} = emqx_ds_shared_sub_leader:register(
Pid, Pid,
@ -111,10 +116,10 @@ do_lookup_leader(Agent, AgentMetadata, TopicFilter, State) ->
?SLOG(info, #{ ?SLOG(info, #{
msg => lookup_leader, msg => lookup_leader,
agent => Agent, agent => Agent,
topic_filter => TopicFilter, share_topic_filter => ShareTopicFilter,
leader => LeaderPid leader => LeaderPid
}), }),
ok = emqx_ds_shared_sub_proto:agent_connect_leader( ok = emqx_ds_shared_sub_proto:agent_connect_leader(
LeaderPid, Agent, AgentMetadata, TopicFilter LeaderPid, Agent, AgentMetadata, ShareTopicFilter
), ),
State. State.

View File

@ -33,9 +33,9 @@ introduced_in() ->
emqx_ds_shared_sub_proto:agent_metadata(), emqx_ds_shared_sub_proto:agent_metadata(),
emqx_persistent_session_ds:share_topic_filter() emqx_persistent_session_ds:share_topic_filter()
) -> ok. ) -> ok.
agent_connect_leader(Node, ToLeader, FromAgent, AgentMetadata, TopicFilter) -> agent_connect_leader(Node, ToLeader, FromAgent, AgentMetadata, ShareTopicFilter) ->
erpc:cast(Node, emqx_ds_shared_sub_proto, agent_connect_leader, [ erpc:cast(Node, emqx_ds_shared_sub_proto, agent_connect_leader, [
ToLeader, FromAgent, AgentMetadata, TopicFilter ToLeader, FromAgent, AgentMetadata, ShareTopicFilter
]). ]).
-spec agent_update_stream_states( -spec agent_update_stream_states(