mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
refactor(gateway): share shard helpers and test fixtures (#3381)
This commit is contained in:
1 parent
f22261f218
commit
e7b13fd19f
25 files changed
+101
-2021
No files matched your search
@@ -1,98 +0,0 @@
|
||||
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
-module(gateway_dispatch_relay_ordering_tests).
|
||||
-typing([eqwalizer]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(STATE_KEY, {gateway_dispatch_relay, state}).
|
||||
-define(EVENT_COUNT, 8).
|
||||
|
||||
dispatch_preserves_order_when_worker_is_backpressured_test_() ->
|
||||
{timeout, 30, fun dispatch_preserves_order_when_worker_is_backpressured/0}.
|
||||
|
||||
dispatch_many_preserves_order_when_worker_is_backpressured_test_() ->
|
||||
{timeout, 30, fun dispatch_many_preserves_order_when_worker_is_backpressured/0}.
|
||||
|
||||
dispatch_preserves_order_when_worker_is_backpressured() ->
|
||||
assert_ordered_delivery(fun(SessionPid, N) ->
|
||||
gateway_dispatch_relay:dispatch(SessionPid, relay_order_event, #{<<"n">> => N})
|
||||
end).
|
||||
|
||||
dispatch_many_preserves_order_when_worker_is_backpressured() ->
|
||||
assert_ordered_delivery(fun(SessionPid, N) ->
|
||||
gateway_dispatch_relay:dispatch_many([SessionPid], relay_order_event, #{<<"n">> => N})
|
||||
end).
|
||||
|
||||
assert_ordered_delivery(SendFun) ->
|
||||
Worker = gateway_dispatch_relay_batch:start_worker(0),
|
||||
try
|
||||
with_relay_workers([Worker], fun() -> ordered_delivery(Worker, SendFun) end)
|
||||
after
|
||||
stop_worker(Worker)
|
||||
end.
|
||||
|
||||
ordered_delivery(Worker, SendFun) ->
|
||||
Ref = make_ref(),
|
||||
Parent = self(),
|
||||
SessionPid = spawn_link(fun() -> session_loop(Parent, Ref) end),
|
||||
MaxQueue = gateway_dispatch_relay_batch:max_queue(),
|
||||
ok = sys:suspend(Worker),
|
||||
fill_queue(Worker, MaxQueue - ?EVENT_COUNT),
|
||||
lists:foreach(fun(N) -> ok = SendFun(SessionPid, N) end, lists:seq(1, ?EVENT_COUNT)),
|
||||
?assert(gateway_dispatch_relay_batch:message_queue_len(Worker) >= MaxQueue),
|
||||
ok = sys:resume(Worker),
|
||||
Observed = collect_observed(Ref, ?EVENT_COUNT, []),
|
||||
SessionPid ! stop,
|
||||
?assertEqual(lists:seq(1, ?EVENT_COUNT), Observed).
|
||||
|
||||
fill_queue(_Worker, 0) ->
|
||||
ok;
|
||||
fill_queue(Worker, Remaining) ->
|
||||
Worker ! relay_order_filler,
|
||||
fill_queue(Worker, Remaining - 1).
|
||||
|
||||
session_loop(Parent, Ref) ->
|
||||
receive
|
||||
{'$gen_cast', {dispatch, relay_order_event, #{<<"n">> := N}}} ->
|
||||
Parent ! {relay_order_received, Ref, N},
|
||||
session_loop(Parent, Ref);
|
||||
stop ->
|
||||
ok
|
||||
after 30000 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
collect_observed(_Ref, 0, Acc) ->
|
||||
lists:reverse(Acc);
|
||||
collect_observed(Ref, Remaining, Acc) ->
|
||||
receive
|
||||
{relay_order_received, Ref, N} -> collect_observed(Ref, Remaining - 1, [N | Acc])
|
||||
after 10000 ->
|
||||
?assert(false, {relay_order_timeout, Remaining, lists:reverse(Acc)})
|
||||
end.
|
||||
|
||||
with_relay_workers(Workers, Fun) ->
|
||||
Previous = persistent_term:get(?STATE_KEY, undefined),
|
||||
persistent_term:put(?STATE_KEY, #{
|
||||
workers => list_to_tuple(Workers),
|
||||
shard_count => length(Workers)
|
||||
}),
|
||||
try
|
||||
Fun()
|
||||
after
|
||||
restore_relay_state(Previous)
|
||||
end.
|
||||
|
||||
restore_relay_state(undefined) ->
|
||||
_ = persistent_term:erase(?STATE_KEY),
|
||||
ok;
|
||||
restore_relay_state(Previous) ->
|
||||
persistent_term:put(?STATE_KEY, Previous).
|
||||
|
||||
stop_worker(Pid) ->
|
||||
try gen_server:stop(Pid, normal, 5000) of
|
||||
_ -> ok
|
||||
catch
|
||||
exit:_ -> ok
|
||||
end.
|
||||
@@ -1,131 +0,0 @@
|
||||
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
-module(session_manager_transfer_stress_tests).
|
||||
-typing([eqwalizer]).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
aggregate_counts_many_shards_mixed_replies_test_() ->
|
||||
{timeout, 15, fun aggregate_counts_many_shards_mixed_replies/0}.
|
||||
|
||||
aggregate_handoff_many_shards_mixed_replies_test_() ->
|
||||
{timeout, 15, fun aggregate_handoff_many_shards_mixed_replies/0}.
|
||||
|
||||
aggregate_counts_many_shards_mixed_replies() ->
|
||||
{Shards, LivePids, ExpectedCount} = build_count_shards(256),
|
||||
State = #{shards => Shards, shard_count => 256},
|
||||
try
|
||||
{ExpectedCount, State} = session_manager_transfer:aggregate_counts(
|
||||
reconnect_drain, State
|
||||
)
|
||||
after
|
||||
stop_live_shards(LivePids)
|
||||
end.
|
||||
|
||||
aggregate_handoff_many_shards_mixed_replies() ->
|
||||
{Shards, LivePids, ExpectedResult} = build_handoff_shards(128),
|
||||
State = #{shards => Shards, shard_count => 128},
|
||||
try
|
||||
{ExpectedResult, State} =
|
||||
session_manager_transfer:aggregate_handoff_to_topology(['peer@host'], State)
|
||||
after
|
||||
stop_live_shards(LivePids)
|
||||
end.
|
||||
|
||||
build_count_shards(Count) ->
|
||||
lists:foldl(
|
||||
fun(Index, {ShardAcc, PidAcc, ExpectedAcc}) ->
|
||||
{Pid, MaybeLive, Expected} = count_shard(Index),
|
||||
{
|
||||
ShardAcc#{Index => #{pid => Pid, ref => make_ref()}},
|
||||
add_live_pid(MaybeLive, Pid, PidAcc),
|
||||
ExpectedAcc + Expected
|
||||
}
|
||||
end,
|
||||
{#{}, [], 0},
|
||||
lists:seq(1, Count)
|
||||
).
|
||||
|
||||
count_shard(Index) ->
|
||||
case Index rem 10 of
|
||||
0 ->
|
||||
dead_shard(0);
|
||||
1 ->
|
||||
{spawn_shard(invalid_reply), live, 0};
|
||||
_ ->
|
||||
{spawn_shard({ok, 1}), live, 1}
|
||||
end.
|
||||
|
||||
build_handoff_shards(Count) ->
|
||||
lists:foldl(
|
||||
fun(Index, {ShardAcc, PidAcc, ExpectedAcc}) ->
|
||||
{Pid, MaybeLive, Expected} = handoff_shard(Index),
|
||||
{
|
||||
ShardAcc#{Index => #{pid => Pid, ref => make_ref()}},
|
||||
add_live_pid(MaybeLive, Pid, PidAcc),
|
||||
sum_handoff(ExpectedAcc, Expected)
|
||||
}
|
||||
end,
|
||||
{#{}, [], #{attempted => 0, handed_off => 0}},
|
||||
lists:seq(1, Count)
|
||||
).
|
||||
|
||||
handoff_shard(Index) ->
|
||||
case Index rem 8 of
|
||||
0 ->
|
||||
dead_shard(#{attempted => 1, handed_off => 0});
|
||||
1 ->
|
||||
{spawn_shard(invalid_reply), live, #{attempted => 1, handed_off => 0}};
|
||||
2 ->
|
||||
{spawn_shard(2), live, #{attempted => 2, handed_off => 2}};
|
||||
_ ->
|
||||
{spawn_shard({ok, #{attempted => 3, handed_off => 2}}), live, #{
|
||||
attempted => 3,
|
||||
handed_off => 2
|
||||
}}
|
||||
end.
|
||||
|
||||
dead_shard(Expected) ->
|
||||
Pid = spawn(fun() -> ok end),
|
||||
wait_for_dead(Pid, 100),
|
||||
{Pid, dead, Expected}.
|
||||
|
||||
spawn_shard(Reply) ->
|
||||
spawn(fun() -> shard_loop(Reply) end).
|
||||
|
||||
shard_loop(Reply) ->
|
||||
receive
|
||||
{'$gen_call', From, _Request} ->
|
||||
gen_server:reply(From, Reply),
|
||||
shard_loop(Reply);
|
||||
stop ->
|
||||
ok
|
||||
after 10000 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
add_live_pid(live, Pid, Pids) ->
|
||||
[Pid | Pids];
|
||||
add_live_pid(dead, _Pid, Pids) ->
|
||||
Pids.
|
||||
|
||||
stop_live_shards(Pids) ->
|
||||
lists:foreach(fun(Pid) -> Pid ! stop end, Pids).
|
||||
|
||||
sum_handoff(Acc, Result) ->
|
||||
#{
|
||||
attempted => maps:get(attempted, Acc) + maps:get(attempted, Result),
|
||||
handed_off => maps:get(handed_off, Acc) + maps:get(handed_off, Result)
|
||||
}.
|
||||
|
||||
wait_for_dead(Pid, Attempts) ->
|
||||
case is_process_alive(Pid) of
|
||||
false ->
|
||||
ok;
|
||||
true when Attempts > 0 ->
|
||||
timer:sleep(1),
|
||||
wait_for_dead(Pid, Attempts - 1);
|
||||
true ->
|
||||
exit(Pid, kill),
|
||||
ok
|
||||
end.
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user