test(shared): add a test case to ensure shared dispatch retry

to ensure retry will not enter a dead loop
This commit is contained in:
Zaiming (Stone) Shi 2022-10-05 22:23:07 +02:00
parent e8279a02ef
commit 1c29e2806a
5 changed files with 53 additions and 13 deletions

View File

@ -10,7 +10,7 @@
* Fix GET /listeners API crash When some nodes still in initial configuration. [#9002](https://github.com/emqx/emqx/pull/9002) * Fix GET /listeners API crash When some nodes still in initial configuration. [#9002](https://github.com/emqx/emqx/pull/9002)
* Fix empty variable interpolation in authentication and authorization. Placeholders for undefined variables are rendered now as empty strings and do not cause errors anymore. [#8963](https://github.com/emqx/emqx/pull/8963) * Fix empty variable interpolation in authentication and authorization. Placeholders for undefined variables are rendered now as empty strings and do not cause errors anymore. [#8963](https://github.com/emqx/emqx/pull/8963)
* Fix the latency statistics error of the slow subscription module when `stats_type` is `internal` or `response`. [#8986](https://github.com/emqx/emqx/pull/8986) * Fix the latency statistics error of the slow subscription module when `stats_type` is `internal` or `response`. [#8986](https://github.com/emqx/emqx/pull/8986)
* Redispatch shared subscription messages. * Redispatch shared subscription messages. [#9104](https://github.com/emqx/emqx/pull/9104)
# 5.0.8 # 5.0.8

View File

@ -997,8 +997,13 @@ maybe_nack(Delivers) ->
lists:filter(fun not_nacked/1, Delivers). lists:filter(fun not_nacked/1, Delivers).
not_nacked({deliver, _Topic, Msg}) -> not_nacked({deliver, _Topic, Msg}) ->
not (emqx_shared_sub:is_ack_required(Msg) andalso case emqx_shared_sub:is_ack_required(Msg) of
(ok == emqx_shared_sub:nack_no_connection(Msg))). true ->
ok = emqx_shared_sub:nack_no_connection(Msg),
false;
false ->
true
end.
maybe_mark_as_delivered(Session, Delivers) -> maybe_mark_as_delivered(Session, Delivers) ->
case emqx_session:info(is_persistent, Session) of case emqx_session:info(is_persistent, Session) of
@ -1222,6 +1227,8 @@ handle_call(
ChanInfo1 = info(NChannel), ChanInfo1 = info(NChannel),
emqx_cm:set_chan_info(ClientId, ChanInfo1#{sockinfo => SockInfo}), emqx_cm:set_chan_info(ClientId, ChanInfo1#{sockinfo => SockInfo}),
reply(ok, reset_timer(alive_timer, NChannel)); reply(ok, reset_timer(alive_timer, NChannel));
handle_call(get_mqueue, Channel) ->
reply({ok, get_mqueue(Channel)}, Channel);
handle_call(Req, Channel) -> handle_call(Req, Channel) ->
?SLOG(error, #{msg => "unexpected_call", call => Req}), ?SLOG(error, #{msg => "unexpected_call", call => Req}),
reply(ignored, Channel). reply(ignored, Channel).
@ -2224,3 +2231,6 @@ get_mqtt_conf(Zone, Key, Default) ->
set_field(Name, Value, Channel) -> set_field(Name, Value, Channel) ->
Pos = emqx_misc:index_of(Name, record_info(fields, channel)), Pos = emqx_misc:index_of(Name, record_info(fields, channel)),
setelement(Pos + 1, Channel, Value). setelement(Pos + 1, Channel, Value).
get_mqueue(#channel{session = Session}) ->
emqx_session:get_mqueue(Session).

View File

@ -60,7 +60,8 @@
info/2, info/2,
is_session/1, is_session/1,
stats/1, stats/1,
obtain_next_pkt_id/1 obtain_next_pkt_id/1,
get_mqueue/1
]). ]).
-export([ -export([
@ -917,3 +918,6 @@ age(Now, Ts) -> Now - Ts.
set_field(Name, Value, Session) -> set_field(Name, Value, Session) ->
Pos = emqx_misc:index_of(Name, record_info(fields, session)), Pos = emqx_misc:index_of(Name, record_info(fields, session)),
setelement(Pos + 1, Session, Value). setelement(Pos + 1, Session, Value).
get_mqueue(#session{mqueue = Q}) ->
emqx_mqueue:to_list(Q).

View File

@ -47,8 +47,7 @@
maybe_ack/1, maybe_ack/1,
maybe_nack_dropped/1, maybe_nack_dropped/1,
nack_no_connection/1, nack_no_connection/1,
is_ack_required/1, is_ack_required/1
get_group/1
]). ]).
%% for testing %% for testing
@ -275,13 +274,6 @@ get_redispatch_to(Msg) ->
-spec is_ack_required(emqx_types:message()) -> boolean(). -spec is_ack_required(emqx_types:message()) -> boolean().
is_ack_required(Msg) -> ?NO_ACK =/= get_group_ack(Msg). is_ack_required(Msg) -> ?NO_ACK =/= get_group_ack(Msg).
-spec get_group(emqx_types:message()) -> {ok, any()} | error.
get_group(Msg) ->
case get_group_ack(Msg) of
?NO_ACK -> error;
{Group, _Sender, _Ref} -> {ok, Group}
end.
%% @doc Negative ack dropped message due to inflight window or message queue being full. %% @doc Negative ack dropped message due to inflight window or message queue being full.
-spec maybe_nack_dropped(emqx_types:message()) -> boolean(). -spec maybe_nack_dropped(emqx_types:message()) -> boolean().
maybe_nack_dropped(Msg) -> maybe_nack_dropped(Msg) ->

View File

@ -678,6 +678,40 @@ test_redispatch_qos1(_Config, AckEnabled) ->
emqtt:stop(UsedSubPid2), emqtt:stop(UsedSubPid2),
ok. ok.
t_qos1_random_dispatch_if_all_members_are_down(Config) when is_list(Config) ->
ok = ensure_config(sticky, true),
Group = <<"group1">>,
Topic = <<"foo/bar">>,
ClientId1 = <<"ClientId1">>,
ClientId2 = <<"ClientId2">>,
SubOpts = [{clean_start, false}],
{ok, ConnPub} = emqtt:start_link([{clientid, <<"pub">>}]),
{ok, _} = emqtt:connect(ConnPub),
{ok, ConnPid1} = emqtt:start_link([{clientid, ClientId1} | SubOpts]),
{ok, ConnPid2} = emqtt:start_link([{clientid, ClientId2} | SubOpts]),
{ok, _} = emqtt:connect(ConnPid1),
{ok, _} = emqtt:connect(ConnPid2),
emqtt:subscribe(ConnPid1, {<<"$share/", Group/binary, "/foo/bar">>, 1}),
emqtt:subscribe(ConnPid2, {<<"$share/", Group/binary, "/foo/bar">>, 1}),
ok = emqtt:stop(ConnPid1),
ok = emqtt:stop(ConnPid2),
[Pid1, Pid2] = emqx_shared_sub:subscribers(Group, Topic),
?assert(is_process_alive(Pid1)),
?assert(is_process_alive(Pid2)),
{ok, _} = emqtt:publish(ConnPub, Topic, <<"hello11">>, 1),
ct:sleep(100),
{ok, Msgs1} = gen_server:call(Pid1, get_mqueue),
{ok, Msgs2} = gen_server:call(Pid2, get_mqueue),
%% assert the message is in mqueue (because socket is closed)
?assertMatch([#message{payload = <<"hello11">>}], Msgs1 ++ Msgs2),
emqtt:stop(ConnPub),
ok.
%% No ack, QoS 2 subscriptions, %% No ack, QoS 2 subscriptions,
%% client1 receives one message, send pubrec, then suspend %% client1 receives one message, send pubrec, then suspend
%% client2 acts normal (auto_ack=true) %% client2 acts normal (auto_ack=true)