test(ds): Use streams to fill the storage
This commit is contained in:
parent
a0a3977043
commit
63e51fca66
|
@ -30,7 +30,7 @@
|
||||||
).
|
).
|
||||||
|
|
||||||
-define(diff_opts, #{
|
-define(diff_opts, #{
|
||||||
context => 20, window => 1000, max_failures => 1000, compare_fun => fun message_eq/2
|
context => 20, window => 1000, compare_fun => fun message_eq/2
|
||||||
}).
|
}).
|
||||||
|
|
||||||
opts() ->
|
opts() ->
|
||||||
|
@ -76,64 +76,59 @@ t_replication_transfers_snapshots('end', Config) ->
|
||||||
ok = emqx_cth_cluster:stop(?config(nodes, Config)).
|
ok = emqx_cth_cluster:stop(?config(nodes, Config)).
|
||||||
|
|
||||||
t_replication_transfers_snapshots(Config) ->
|
t_replication_transfers_snapshots(Config) ->
|
||||||
NMsgs = 4000,
|
NMsgs = 400,
|
||||||
|
NClients = 5,
|
||||||
|
{Stream, TopicStreams} = interleaved_topic_messages(?FUNCTION_NAME, NClients, NMsgs),
|
||||||
|
|
||||||
Nodes = [Node, NodeOffline | _] = ?config(nodes, Config),
|
Nodes = [Node, NodeOffline | _] = ?config(nodes, Config),
|
||||||
_Specs = [_, SpecOffline | _] = ?config(specs, Config),
|
_Specs = [_, SpecOffline | _] = ?config(specs, Config),
|
||||||
|
?check_trace(
|
||||||
|
begin
|
||||||
|
%% Initialize DB on all nodes and wait for it to be online.
|
||||||
|
Opts = opts(#{n_shards => 1, n_sites => 3}),
|
||||||
|
?assertEqual(
|
||||||
|
[{ok, ok} || _ <- Nodes],
|
||||||
|
erpc:multicall(Nodes, emqx_ds, open_db, [?DB, Opts])
|
||||||
|
),
|
||||||
|
?retry(
|
||||||
|
500,
|
||||||
|
10,
|
||||||
|
?assertMatch([[_], [_], [_]], [shards_online(N, ?DB) || N <- Nodes])
|
||||||
|
),
|
||||||
|
|
||||||
%% Initialize DB on all nodes and wait for it to be online.
|
%% Stop the DB on the "offline" node.
|
||||||
Opts = opts(#{n_shards => 1, n_sites => 3}),
|
ok = emqx_cth_cluster:stop_node(NodeOffline),
|
||||||
?assertEqual(
|
|
||||||
[{ok, ok} || _ <- Nodes],
|
|
||||||
erpc:multicall(Nodes, emqx_ds, open_db, [?DB, Opts])
|
|
||||||
),
|
|
||||||
?retry(
|
|
||||||
500,
|
|
||||||
10,
|
|
||||||
?assertMatch([[_], [_], [_]], [shards_online(N, ?DB) || N <- Nodes])
|
|
||||||
),
|
|
||||||
|
|
||||||
%% Stop the DB on the "offline" node.
|
%% Fill the storage with messages and few additional generations.
|
||||||
ok = emqx_cth_cluster:stop_node(NodeOffline),
|
apply_stream(?DB, Nodes -- [NodeOffline], Stream),
|
||||||
|
|
||||||
%% Fill the storage with messages and few additional generations.
|
%% Restart the node.
|
||||||
Messages = fill_storage(Node, ?DB, NMsgs, #{p_addgen => 0.01}),
|
[NodeOffline] = emqx_cth_cluster:restart(SpecOffline),
|
||||||
|
{ok, SRef} = snabbkaffe:subscribe(
|
||||||
|
?match_event(#{
|
||||||
|
?snk_kind := dsrepl_snapshot_accepted,
|
||||||
|
?snk_meta := #{node := NodeOffline}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
?assertEqual(
|
||||||
|
ok,
|
||||||
|
erpc:call(NodeOffline, emqx_ds, open_db, [?DB, opts()])
|
||||||
|
),
|
||||||
|
|
||||||
%% Restart the node.
|
%% Trigger storage operation and wait the replica to be restored.
|
||||||
[NodeOffline] = emqx_cth_cluster:restart(SpecOffline),
|
_ = add_generation(Node, ?DB),
|
||||||
{ok, SRef} = snabbkaffe:subscribe(
|
?assertMatch(
|
||||||
?match_event(#{
|
{ok, _},
|
||||||
?snk_kind := dsrepl_snapshot_accepted,
|
snabbkaffe:receive_events(SRef)
|
||||||
?snk_meta := #{node := NodeOffline}
|
),
|
||||||
})
|
|
||||||
),
|
|
||||||
?assertEqual(
|
|
||||||
ok,
|
|
||||||
erpc:call(NodeOffline, emqx_ds, open_db, [?DB, opts()])
|
|
||||||
),
|
|
||||||
|
|
||||||
%% Trigger storage operation and wait the replica to be restored.
|
%% Wait until any pending replication activities are finished (e.g. Raft log entries).
|
||||||
_ = add_generation(Node, ?DB),
|
ok = timer:sleep(3_000),
|
||||||
?assertMatch(
|
|
||||||
{ok, _},
|
|
||||||
snabbkaffe:receive_events(SRef)
|
|
||||||
),
|
|
||||||
|
|
||||||
%% Wait until any pending replication activities are finished (e.g. Raft log entries).
|
%% Check that the DB has been restored:
|
||||||
ok = timer:sleep(3_000),
|
verify_stream_effects(?FUNCTION_NAME, Nodes, TopicStreams)
|
||||||
|
end,
|
||||||
%% Check that the DB has been restored.
|
[]
|
||||||
Shard = hd(shards(NodeOffline, ?DB)),
|
|
||||||
MessagesOffline = lists:keysort(
|
|
||||||
#message.timestamp,
|
|
||||||
consume_shard(NodeOffline, ?DB, Shard, ['#'], 0)
|
|
||||||
),
|
|
||||||
?assertEqual(
|
|
||||||
sample(40, Messages),
|
|
||||||
sample(40, MessagesOffline)
|
|
||||||
),
|
|
||||||
?assertEqual(
|
|
||||||
Messages,
|
|
||||||
MessagesOffline
|
|
||||||
).
|
).
|
||||||
|
|
||||||
t_rebalance(init, Config) ->
|
t_rebalance(init, Config) ->
|
||||||
|
@ -159,50 +154,13 @@ t_rebalance('end', Config) ->
|
||||||
t_rebalance(Config) ->
|
t_rebalance(Config) ->
|
||||||
NMsgs = 50,
|
NMsgs = 50,
|
||||||
NClients = 5,
|
NClients = 5,
|
||||||
%% List of fake client IDs:
|
{Stream0, TopicStreams} = interleaved_topic_messages(?FUNCTION_NAME, NClients, NMsgs),
|
||||||
Clients = [integer_to_binary(I) || I <- lists:seq(1, NClients)],
|
Nodes = [N1, N2 | _] = ?config(nodes, Config),
|
||||||
%% List of streams that generate messages for each "client" in its own topic:
|
|
||||||
TopicStreams = [
|
|
||||||
{ClientId, emqx_utils_stream:limit_length(NMsgs, topic_messages(?FUNCTION_NAME, ClientId))}
|
|
||||||
|| ClientId <- Clients
|
|
||||||
],
|
|
||||||
%% Interleaved list of events:
|
|
||||||
Stream0 = emqx_utils_stream:interleave(
|
|
||||||
[{2, Stream} || {_ClientId, Stream} <- TopicStreams], true
|
|
||||||
),
|
|
||||||
Stream = emqx_utils_stream:interleave(
|
|
||||||
[
|
|
||||||
{50, Stream0},
|
|
||||||
emqx_utils_stream:const(add_generation)
|
|
||||||
],
|
|
||||||
false
|
|
||||||
),
|
|
||||||
Nodes = [N1, N2, N3, N4] = ?config(nodes, Config),
|
|
||||||
?check_trace(
|
?check_trace(
|
||||||
#{timetrap => 30_000},
|
#{timetrap => 30_000},
|
||||||
begin
|
begin
|
||||||
%% 0. Inject schedulings to make sure the messages are
|
|
||||||
%% written to the storage before, during, and after
|
|
||||||
%% rebalance:
|
|
||||||
?force_ordering(
|
|
||||||
#{?snk_kind := test_push_message, n := 10},
|
|
||||||
#{?snk_kind := test_start_rebalance}
|
|
||||||
),
|
|
||||||
?force_ordering(
|
|
||||||
#{?snk_kind := test_start_rebalance1},
|
|
||||||
#{?snk_kind := test_push_message, n := 20}
|
|
||||||
),
|
|
||||||
?force_ordering(
|
|
||||||
#{?snk_kind := test_push_message, n := 30},
|
|
||||||
#{?snk_kind := test_start_rebalance2}
|
|
||||||
),
|
|
||||||
?force_ordering(
|
|
||||||
#{?snk_kind := test_end_rebalance},
|
|
||||||
#{?snk_kind := test_push_message, n := 40}
|
|
||||||
),
|
|
||||||
%% 1. Initialize DB on the first node.
|
%% 1. Initialize DB on the first node.
|
||||||
Opts = opts(#{n_shards => 16, n_sites => 1, replication_factor => 3}),
|
Opts = opts(#{n_shards => 16, n_sites => 1, replication_factor => 3}),
|
||||||
|
|
||||||
?assertEqual(ok, ?ON(N1, emqx_ds:open_db(?DB, Opts))),
|
?assertEqual(ok, ?ON(N1, emqx_ds:open_db(?DB, Opts))),
|
||||||
?assertMatch(Shards when length(Shards) == 16, shards_online(N1, ?DB)),
|
?assertMatch(Shards when length(Shards) == 16, shards_online(N1, ?DB)),
|
||||||
|
|
||||||
|
@ -215,6 +173,22 @@ t_rebalance(Config) ->
|
||||||
Sites = [S1, S2 | _] = [ds_repl_meta(N, this_site) || N <- Nodes],
|
Sites = [S1, S2 | _] = [ds_repl_meta(N, this_site) || N <- Nodes],
|
||||||
ct:pal("Sites: ~p~n", [Sites]),
|
ct:pal("Sites: ~p~n", [Sites]),
|
||||||
|
|
||||||
|
Sequence = [
|
||||||
|
%% Join the second site to the DB replication sites:
|
||||||
|
{N1, join_db_site, S2},
|
||||||
|
%% Should be a no-op:
|
||||||
|
{N2, join_db_site, S2},
|
||||||
|
%% Now join the rest of the sites:
|
||||||
|
{N2, assign_db_sites, Sites}
|
||||||
|
],
|
||||||
|
Stream = emqx_utils_stream:interleave(
|
||||||
|
[
|
||||||
|
{50, Stream0},
|
||||||
|
emqx_utils_stream:list(Sequence)
|
||||||
|
],
|
||||||
|
true
|
||||||
|
),
|
||||||
|
|
||||||
%% 1.2 Verify that all nodes have the same view of metadata storage:
|
%% 1.2 Verify that all nodes have the same view of metadata storage:
|
||||||
[
|
[
|
||||||
?defer_assert(
|
?defer_assert(
|
||||||
|
@ -231,31 +205,9 @@ t_rebalance(Config) ->
|
||||||
],
|
],
|
||||||
|
|
||||||
%% 2. Start filling the storage:
|
%% 2. Start filling the storage:
|
||||||
spawn_link(
|
apply_stream(?DB, Nodes, Stream),
|
||||||
fun() ->
|
timer:sleep(5000),
|
||||||
NodeStream = emqx_utils_stream:repeat(emqx_utils_stream:list(Nodes)),
|
verify_stream_effects(?FUNCTION_NAME, Nodes, TopicStreams),
|
||||||
apply_stream(?DB, NodeStream, Stream, 0)
|
|
||||||
end
|
|
||||||
),
|
|
||||||
|
|
||||||
%% 3. Start rebalance in the meanwhile:
|
|
||||||
?tp(test_start_rebalance1, #{}),
|
|
||||||
%% 3.1 Join the second site to the DB replication sites.
|
|
||||||
?assertEqual(ok, ?ON(N1, emqx_ds_replication_layer_meta:join_db_site(?DB, S2))),
|
|
||||||
%% Should be no-op.
|
|
||||||
?assertEqual(ok, ?ON(N2, emqx_ds_replication_layer_meta:join_db_site(?DB, S2))),
|
|
||||||
ct:pal("Transitions (~p -> ~p): ~p~n", [[S1], [S1, S2], transitions(N1, ?DB)]),
|
|
||||||
|
|
||||||
?retry(1000, 10, ?assertEqual([], transitions(N1, ?DB))),
|
|
||||||
|
|
||||||
?tp(test_start_rebalance2, #{}),
|
|
||||||
%% Now join the rest of the sites.
|
|
||||||
?assertEqual(ok, ds_repl_meta(N2, assign_db_sites, [?DB, Sites])),
|
|
||||||
ct:pal("Transitions (~p -> ~p): ~p~n", [[S1, S2], Sites, transitions(N1, ?DB)]),
|
|
||||||
|
|
||||||
?retry(1000, 10, ?assertEqual([], transitions(N2, ?DB))),
|
|
||||||
|
|
||||||
?tp(test_end_rebalance, #{}),
|
|
||||||
[
|
[
|
||||||
?defer_assert(
|
?defer_assert(
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
|
@ -279,17 +231,12 @@ t_rebalance(Config) ->
|
||||||
ShardServers
|
ShardServers
|
||||||
),
|
),
|
||||||
|
|
||||||
%% Verify that the messages are preserved after the rebalance:
|
|
||||||
?block_until(#{?snk_kind := all_done}),
|
|
||||||
timer:sleep(5000),
|
|
||||||
verify_stream_effects(?FUNCTION_NAME, Nodes, TopicStreams),
|
|
||||||
|
|
||||||
%% Scale down the cluster by removing the first node.
|
%% Scale down the cluster by removing the first node.
|
||||||
?assertEqual(ok, ds_repl_meta(N1, leave_db_site, [?DB, S1])),
|
?assertEqual(ok, ds_repl_meta(N1, leave_db_site, [?DB, S1])),
|
||||||
ct:pal("Transitions (~p -> ~p): ~p~n", [Sites, tl(Sites), transitions(N1, ?DB)]),
|
ct:pal("Transitions (~p -> ~p): ~p~n", [Sites, tl(Sites), transitions(N1, ?DB)]),
|
||||||
?retry(1000, 10, ?assertEqual([], transitions(N2, ?DB))),
|
?retry(1000, 10, ?assertEqual([], transitions(N2, ?DB))),
|
||||||
|
|
||||||
%% Verify that each node is now responsible for each shard.
|
%% Verify that at the end each node is now responsible for each shard.
|
||||||
?defer_assert(
|
?defer_assert(
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
[0, 16, 16, 16],
|
[0, 16, 16, 16],
|
||||||
|
@ -387,81 +334,77 @@ t_rebalance_chaotic_converges(Config) ->
|
||||||
NMsgs = 500,
|
NMsgs = 500,
|
||||||
Nodes = [N1, N2, N3] = ?config(nodes, Config),
|
Nodes = [N1, N2, N3] = ?config(nodes, Config),
|
||||||
|
|
||||||
%% Initialize DB on first two nodes.
|
NClients = 5,
|
||||||
Opts = opts(#{n_shards => 16, n_sites => 2, replication_factor => 3}),
|
{Stream0, TopicStreams} = interleaved_topic_messages(?FUNCTION_NAME, NClients, NMsgs),
|
||||||
?assertEqual(
|
|
||||||
[{ok, ok}, {ok, ok}],
|
|
||||||
erpc:multicall([N1, N2], emqx_ds, open_db, [?DB, Opts])
|
|
||||||
),
|
|
||||||
|
|
||||||
%% Open DB on the last node.
|
?check_trace(
|
||||||
?assertEqual(
|
#{},
|
||||||
ok,
|
begin
|
||||||
erpc:call(N3, emqx_ds, open_db, [?DB, Opts])
|
%% Initialize DB on first two nodes.
|
||||||
),
|
Opts = opts(#{n_shards => 16, n_sites => 2, replication_factor => 3}),
|
||||||
|
|
||||||
%% Find out which sites there are.
|
?assertEqual(
|
||||||
Sites = [S1, S2, S3] = [ds_repl_meta(N, this_site) || N <- Nodes],
|
[{ok, ok}, {ok, ok}],
|
||||||
ct:pal("Sites: ~p~n", [Sites]),
|
erpc:multicall([N1, N2], emqx_ds, open_db, [?DB, Opts])
|
||||||
|
),
|
||||||
|
|
||||||
%% Initially, the DB is assigned to [S1, S2].
|
%% Open DB on the last node.
|
||||||
?retry(500, 10, ?assertEqual([16, 16], [n_shards_online(N, ?DB) || N <- [N1, N2]])),
|
?assertEqual(
|
||||||
?assertEqual(
|
ok,
|
||||||
lists:sort([S1, S2]),
|
erpc:call(N3, emqx_ds, open_db, [?DB, Opts])
|
||||||
ds_repl_meta(N1, db_sites, [?DB])
|
),
|
||||||
),
|
|
||||||
|
|
||||||
%% Fill the storage with messages and few additional generations.
|
%% Find out which sites there are.
|
||||||
Messages0 = lists:append([
|
Sites = [S1, S2, S3] = [ds_repl_meta(N, this_site) || N <- Nodes],
|
||||||
fill_storage(N1, ?DB, NMsgs, #{client_id => <<"C1">>}),
|
ct:pal("Sites: ~p~n", [Sites]),
|
||||||
fill_storage(N2, ?DB, NMsgs, #{client_id => <<"C2">>}),
|
|
||||||
fill_storage(N3, ?DB, NMsgs, #{client_id => <<"C3">>})
|
|
||||||
]),
|
|
||||||
|
|
||||||
%% Construct a chaotic transition sequence that changes assignment to [S2, S3].
|
Sequence = [
|
||||||
Sequence = [
|
{N1, join_db_site, S3},
|
||||||
{N1, join_db_site, S3},
|
{N2, leave_db_site, S2},
|
||||||
{N2, leave_db_site, S2},
|
{N3, leave_db_site, S1},
|
||||||
{N3, leave_db_site, S1},
|
{N1, join_db_site, S2},
|
||||||
{N1, join_db_site, S2},
|
{N2, join_db_site, S1},
|
||||||
{N2, join_db_site, S1},
|
{N3, leave_db_site, S3},
|
||||||
{N3, leave_db_site, S3},
|
{N1, leave_db_site, S1},
|
||||||
{N1, leave_db_site, S1},
|
{N2, join_db_site, S3}
|
||||||
{N2, join_db_site, S3}
|
],
|
||||||
],
|
|
||||||
|
|
||||||
%% Apply the sequence while also filling the storage with messages.
|
%% Interleaved list of events:
|
||||||
TransitionMessages = lists:map(
|
Stream = emqx_utils_stream:interleave(
|
||||||
fun({N, Operation, Site}) ->
|
[
|
||||||
%% Apply the transition.
|
{50, Stream0},
|
||||||
?assertEqual(ok, ds_repl_meta(N, Operation, [?DB, Site])),
|
emqx_utils_stream:list(Sequence)
|
||||||
%% Give some time for at least one transition to complete.
|
],
|
||||||
Transitions = transitions(N, ?DB),
|
true
|
||||||
ct:pal("Transitions after ~p: ~p", [Operation, Transitions]),
|
),
|
||||||
?retry(200, 10, ?assertNotEqual(Transitions, transitions(N, ?DB))),
|
|
||||||
%% Fill the storage with messages.
|
?retry(500, 10, ?assertEqual([16, 16], [n_shards_online(N, ?DB) || N <- [N1, N2]])),
|
||||||
CID = integer_to_binary(erlang:system_time()),
|
?assertEqual(
|
||||||
fill_storage(N, ?DB, NMsgs, #{client_id => CID})
|
lists:sort([S1, S2]),
|
||||||
|
ds_repl_meta(N1, db_sites, [?DB]),
|
||||||
|
"Initially, the DB is assigned to [S1, S2]"
|
||||||
|
),
|
||||||
|
|
||||||
|
apply_stream(?DB, Nodes, Stream),
|
||||||
|
|
||||||
|
%% Wait for the last transition to complete.
|
||||||
|
?retry(500, 20, ?assertEqual([], transitions(N1, ?DB))),
|
||||||
|
|
||||||
|
?defer_assert(
|
||||||
|
?assertEqual(
|
||||||
|
lists:sort([S2, S3]),
|
||||||
|
ds_repl_meta(N1, db_sites, [?DB])
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
%% Wait until the LTS timestamp is updated:
|
||||||
|
timer:sleep(5000),
|
||||||
|
|
||||||
|
%% Check that all messages are still there.
|
||||||
|
verify_stream_effects(?FUNCTION_NAME, Nodes, TopicStreams)
|
||||||
end,
|
end,
|
||||||
Sequence
|
[]
|
||||||
),
|
).
|
||||||
|
|
||||||
%% Wait for the last transition to complete.
|
|
||||||
?retry(500, 20, ?assertEqual([], transitions(N1, ?DB))),
|
|
||||||
|
|
||||||
?assertEqual(
|
|
||||||
lists:sort([S2, S3]),
|
|
||||||
ds_repl_meta(N1, db_sites, [?DB])
|
|
||||||
),
|
|
||||||
|
|
||||||
%% Wait until the LTS timestamp is updated
|
|
||||||
timer:sleep(5000),
|
|
||||||
|
|
||||||
%% Check that all messages are still there.
|
|
||||||
Messages = lists:append(TransitionMessages) ++ Messages0,
|
|
||||||
MessagesDB = lists:sort(fun compare_message/2, consume(N1, ?DB, ['#'], 0)),
|
|
||||||
?assertEqual(sample(20, Messages), sample(20, MessagesDB)),
|
|
||||||
?assertEqual(Messages, MessagesDB).
|
|
||||||
|
|
||||||
t_rebalance_offline_restarts(init, Config) ->
|
t_rebalance_offline_restarts(init, Config) ->
|
||||||
Apps = [appspec(emqx_durable_storage)],
|
Apps = [appspec(emqx_durable_storage)],
|
||||||
|
@ -535,7 +478,15 @@ ds_repl_meta(Node, Fun) ->
|
||||||
ds_repl_meta(Node, Fun, []).
|
ds_repl_meta(Node, Fun, []).
|
||||||
|
|
||||||
ds_repl_meta(Node, Fun, Args) ->
|
ds_repl_meta(Node, Fun, Args) ->
|
||||||
erpc:call(Node, emqx_ds_replication_layer_meta, Fun, Args).
|
try
|
||||||
|
erpc:call(Node, emqx_ds_replication_layer_meta, Fun, Args)
|
||||||
|
catch
|
||||||
|
EC:Err:Stack ->
|
||||||
|
ct:pal("emqx_ds_replication_layer_meta:~p(~p) @~p failed:~n~p:~p~nStack: ~p", [
|
||||||
|
Fun, Args, Node, EC, Err, Stack
|
||||||
|
]),
|
||||||
|
error(meta_op_failed)
|
||||||
|
end.
|
||||||
|
|
||||||
ds_repl_shard(Node, Fun, Args) ->
|
ds_repl_shard(Node, Fun, Args) ->
|
||||||
erpc:call(Node, emqx_ds_replication_layer_shard, Fun, Args).
|
erpc:call(Node, emqx_ds_replication_layer_shard, Fun, Args).
|
||||||
|
@ -553,27 +504,6 @@ shards_online(Node, DB) ->
|
||||||
n_shards_online(Node, DB) ->
|
n_shards_online(Node, DB) ->
|
||||||
length(shards_online(Node, DB)).
|
length(shards_online(Node, DB)).
|
||||||
|
|
||||||
fill_storage(Node, DB, NMsgs, Opts) ->
|
|
||||||
fill_storage(Node, DB, NMsgs, 0, Opts).
|
|
||||||
|
|
||||||
fill_storage(Node, DB, NMsgs, I, Opts) when I < NMsgs ->
|
|
||||||
PAddGen = maps:get(p_addgen, Opts, 0.001),
|
|
||||||
R1 = push_message(Node, DB, I, Opts),
|
|
||||||
%probably(PAddGen, fun() -> add_generation(Node, DB) end),
|
|
||||||
R2 = [],
|
|
||||||
R1 ++ R2 ++ fill_storage(Node, DB, NMsgs, I + 1, Opts);
|
|
||||||
fill_storage(_Node, _DB, NMsgs, NMsgs, _Opts) ->
|
|
||||||
[].
|
|
||||||
|
|
||||||
push_message(Node, DB, I, Opts) ->
|
|
||||||
Topic = emqx_topic:join([<<"topic">>, <<"foo">>, integer_to_binary(I)]),
|
|
||||||
%% {Bytes, _} = rand:bytes_s(5, rand:seed_s(default, I)),
|
|
||||||
Bytes = integer_to_binary(I),
|
|
||||||
ClientId = maps:get(client_id, Opts, <<?MODULE_STRING>>),
|
|
||||||
Message = message(ClientId, Topic, Bytes, I * 100),
|
|
||||||
ok = erpc:call(Node, emqx_ds, store_batch, [DB, [Message], #{sync => true}]),
|
|
||||||
[Message].
|
|
||||||
|
|
||||||
add_generation(Node, DB) ->
|
add_generation(Node, DB) ->
|
||||||
ok = erpc:call(Node, emqx_ds, add_generation, [DB]),
|
ok = erpc:call(Node, emqx_ds, add_generation, [DB]),
|
||||||
[].
|
[].
|
||||||
|
@ -674,7 +604,7 @@ do_ds_topic_generation_stream(Node, Shard, It0) ->
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
of
|
of
|
||||||
{ok, It, []} ->
|
{ok, _It, []} ->
|
||||||
[];
|
[];
|
||||||
{ok, end_of_stream} ->
|
{ok, end_of_stream} ->
|
||||||
[];
|
[];
|
||||||
|
@ -685,11 +615,19 @@ do_ds_topic_generation_stream(Node, Shard, It0) ->
|
||||||
|
|
||||||
%% Payload generation:
|
%% Payload generation:
|
||||||
|
|
||||||
|
apply_stream(DB, Nodes, Stream) ->
|
||||||
|
apply_stream(
|
||||||
|
DB,
|
||||||
|
emqx_utils_stream:repeat(emqx_utils_stream:list(Nodes)),
|
||||||
|
Stream,
|
||||||
|
0
|
||||||
|
).
|
||||||
|
|
||||||
apply_stream(DB, NodeStream0, Stream0, N) ->
|
apply_stream(DB, NodeStream0, Stream0, N) ->
|
||||||
case emqx_utils_stream:next(Stream0) of
|
case emqx_utils_stream:next(Stream0) of
|
||||||
[] ->
|
[] ->
|
||||||
?tp(all_done, #{});
|
?tp(all_done, #{});
|
||||||
[Msg = #message{from = From} | Stream] ->
|
[Msg = #message{} | Stream] ->
|
||||||
[Node | NodeStream] = emqx_utils_stream:next(NodeStream0),
|
[Node | NodeStream] = emqx_utils_stream:next(NodeStream0),
|
||||||
?tp(
|
?tp(
|
||||||
test_push_message,
|
test_push_message,
|
||||||
|
@ -701,12 +639,40 @@ apply_stream(DB, NodeStream0, Stream0, N) ->
|
||||||
?ON(Node, emqx_ds:store_batch(DB, [Msg], #{sync => true})),
|
?ON(Node, emqx_ds:store_batch(DB, [Msg], #{sync => true})),
|
||||||
apply_stream(DB, NodeStream, Stream, N + 1);
|
apply_stream(DB, NodeStream, Stream, N + 1);
|
||||||
[add_generation | Stream] ->
|
[add_generation | Stream] ->
|
||||||
[Node | NodeStream] = emqx_utils_stream:next(NodeStream0),
|
%% FIXME:
|
||||||
|
[_Node | NodeStream] = emqx_utils_stream:next(NodeStream0),
|
||||||
%% add_generation(Node, DB),
|
%% add_generation(Node, DB),
|
||||||
apply_stream(DB, NodeStream, Stream, N)
|
apply_stream(DB, NodeStream, Stream, N);
|
||||||
|
[{Node, Operation, Arg} | Stream] when
|
||||||
|
Operation =:= join_db_site; Operation =:= leave_db_site; Operation =:= assign_db_sites
|
||||||
|
->
|
||||||
|
?tp(notice, test_apply_operation, #{node => Node, operation => Operation, arg => Arg}),
|
||||||
|
%% Apply the transition.
|
||||||
|
?assertEqual(ok, ds_repl_meta(Node, Operation, [DB, Arg])),
|
||||||
|
%% Give some time for at least one transition to complete.
|
||||||
|
Transitions = transitions(Node, ?DB),
|
||||||
|
ct:pal("Transitions after ~p: ~p", [Operation, Transitions]),
|
||||||
|
?retry(200, 10, ?assertNotEqual(Transitions, transitions(Node, DB))),
|
||||||
|
apply_stream(DB, NodeStream0, Stream, N);
|
||||||
|
[Fun | Stream] when is_function(Fun) ->
|
||||||
|
Fun(),
|
||||||
|
apply_stream(DB, NodeStream0, Stream, N)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Create an infinite list of messages from a given client:
|
%% @doc Create an infinite list of messages from a given client:
|
||||||
|
interleaved_topic_messages(TestCase, NClients, NMsgs) ->
|
||||||
|
%% List of fake client IDs:
|
||||||
|
Clients = [integer_to_binary(I) || I <- lists:seq(1, NClients)],
|
||||||
|
TopicStreams = [
|
||||||
|
{ClientId, emqx_utils_stream:limit_length(NMsgs, topic_messages(TestCase, ClientId))}
|
||||||
|
|| ClientId <- Clients
|
||||||
|
],
|
||||||
|
%% Interleaved stream of messages:
|
||||||
|
Stream = emqx_utils_stream:interleave(
|
||||||
|
[{2, Stream} || {_ClientId, Stream} <- TopicStreams], true
|
||||||
|
),
|
||||||
|
{Stream, TopicStreams}.
|
||||||
|
|
||||||
topic_messages(TestCase, ClientId) ->
|
topic_messages(TestCase, ClientId) ->
|
||||||
topic_messages(TestCase, ClientId, 0).
|
topic_messages(TestCase, ClientId, 0).
|
||||||
|
|
||||||
|
@ -726,7 +692,7 @@ client_topic(TestCase, ClientId) when is_atom(TestCase) ->
|
||||||
client_topic(TestCase, ClientId) when is_binary(TestCase) ->
|
client_topic(TestCase, ClientId) when is_binary(TestCase) ->
|
||||||
<<TestCase/binary, "/", ClientId/binary>>.
|
<<TestCase/binary, "/", ClientId/binary>>.
|
||||||
|
|
||||||
message_eq(Msg1, {Key, Msg2}) ->
|
message_eq(Msg1, {_Key, Msg2}) ->
|
||||||
%% Timestamps can be modified by the replication layer, ignore them:
|
%% Timestamps can be modified by the replication layer, ignore them:
|
||||||
Msg1#message{timestamp = 0} =:= Msg2#message{timestamp = 0}.
|
Msg1#message{timestamp = 0} =:= Msg2#message{timestamp = 0}.
|
||||||
|
|
||||||
|
@ -734,7 +700,7 @@ message_eq(Msg1, {Key, Msg2}) ->
|
||||||
|
|
||||||
-spec verify_stream_effects(binary(), [node()], [{emqx_types:clientid(), ds_stream()}]) -> ok.
|
-spec verify_stream_effects(binary(), [node()], [{emqx_types:clientid(), ds_stream()}]) -> ok.
|
||||||
verify_stream_effects(TestCase, Nodes0, L) ->
|
verify_stream_effects(TestCase, Nodes0, L) ->
|
||||||
lists:foreach(
|
Checked = lists:flatmap(
|
||||||
fun({ClientId, Stream}) ->
|
fun({ClientId, Stream}) ->
|
||||||
Nodes = nodes_of_clientid(ClientId, Nodes0),
|
Nodes = nodes_of_clientid(ClientId, Nodes0),
|
||||||
ct:pal("Nodes allocated for client ~p: ~p", [ClientId, Nodes]),
|
ct:pal("Nodes allocated for client ~p: ~p", [ClientId, Nodes]),
|
||||||
|
@ -744,7 +710,8 @@ verify_stream_effects(TestCase, Nodes0, L) ->
|
||||||
[verify_stream_effects(TestCase, Node, ClientId, Stream) || Node <- Nodes]
|
[verify_stream_effects(TestCase, Node, ClientId, Stream) || Node <- Nodes]
|
||||||
end,
|
end,
|
||||||
L
|
L
|
||||||
).
|
),
|
||||||
|
?defer_assert(?assertMatch([_ | _], Checked, "Some messages have been verified")).
|
||||||
|
|
||||||
-spec verify_stream_effects(binary(), node(), emqx_types:clientid(), ds_stream()) -> ok.
|
-spec verify_stream_effects(binary(), node(), emqx_types:clientid(), ds_stream()) -> ok.
|
||||||
verify_stream_effects(TestCase, Node, ClientId, ExpectedStream) ->
|
verify_stream_effects(TestCase, Node, ClientId, ExpectedStream) ->
|
||||||
|
|
|
@ -162,7 +162,7 @@ interleave_test() ->
|
||||||
S2 = emqx_utils_stream:list([a, b, c, d]),
|
S2 = emqx_utils_stream:list([a, b, c, d]),
|
||||||
?assertEqual(
|
?assertEqual(
|
||||||
[1, 2, a, b, 3, c, d],
|
[1, 2, a, b, 3, c, d],
|
||||||
emqx_utils_stream:consume(emqx_utils_stream:interleave([{2, S1}, {2, S2}]))
|
emqx_utils_stream:consume(emqx_utils_stream:interleave([{2, S1}, {2, S2}], true))
|
||||||
).
|
).
|
||||||
|
|
||||||
csv_test() ->
|
csv_test() ->
|
||||||
|
|
Loading…
Reference in New Issue