Skip to content

Commit 937453e

Browse files
add missing listener stop
1 parent 503de38 commit 937453e

1 file changed

Lines changed: 36 additions & 5 deletions

File tree

test/pool_SUITE.erl

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ end_per_suite(_) ->
6262
max_streams_h2_size_1_retry_least_loaded,
6363
max_streams_h2_size_2_least_loaded,
6464
max_streams_h2_size_2_retry_least_loaded,
65-
reconnect_h1_least_loaded
65+
reconnect_h1_least_loaded,
66+
least_loaded_routing_least_loaded
6667
],
6768
_ = [cowboy:stop_listener(Listener) || Listener <- ExtraListeners],
6869
ok.
@@ -491,7 +492,14 @@ do_degraded_configuration_error(Config) ->
491492
least_loaded_routing(Config) ->
492493
doc("Confirm the least_loaded strategy always routes to the connection "
493494
"with the fewest active streams."),
494-
Port = config(port, Config),
495+
%% Use a dedicated listener with a 30s delay so the busy connection
496+
%% cannot free up before the test completes on slow CI runners.
497+
Listener = listener_name(?FUNCTION_NAME, Config),
498+
Routes = [{"/", hello_h, []}, {"/delay", delayed_hello_h, 30000}],
499+
{ok, _} = cowboy:start_clear(Listener, [], #{
500+
env => #{dispatch => cowboy_router:compile([{'_', Routes}])}
501+
}),
502+
Port = ranch:get_port(Listener),
495503
Authority = ["localhost:", integer_to_binary(Port)],
496504
{ok, ManagerPid} = gun_pool:start_pool("localhost", Port, #{
497505
conn_opts => #{protocols => [http2]},
@@ -504,15 +512,19 @@ least_loaded_routing(Config) ->
504512
{async, {BusyConn, _}} = gun_pool:get("/delay",
505513
#{<<"host">> => Authority}, #{scope => scope(?FUNCTION_NAME, Config)}),
506514
%% Send requests one at a time and await each before the next.
507-
%% This keeps the idle connection at 0-1 streams, always below the
508-
%% busy connection, so least_loaded must consistently pick it.
515+
%% The stream is released asynchronously (the event handler casts
516+
%% {release_stream, _} to the manager), so wait until the idle
517+
%% connection is back to 0 streams before the next request. It is
518+
%% then strictly below the busy connection, so least_loaded must
519+
%% consistently pick it.
509520
%% With random selection this would pass with probability ~0.1%.
510521
_ = [begin
511522
{async, {ConnPid, _} = PoolStreamRef} = gun_pool:get("/",
512523
#{<<"host">> => Authority}, #{scope => scope(?FUNCTION_NAME, Config)}),
513524
true = ConnPid =/= BusyConn,
514525
{response, nofin, 200, _} = gun_pool:await(PoolStreamRef),
515-
{ok, <<"Hello world!">>} = gun_pool:await_body(PoolStreamRef)
526+
{ok, <<"Hello world!">>} = gun_pool:await_body(PoolStreamRef),
527+
ok = wait_streams_released_except(ManagerPid, BusyConn)
516528
end || _ <- lists:seq(1, 10)].
517529

518530
least_loaded_round_robin(Config) ->
@@ -622,6 +634,25 @@ wait_for_metrics(Authority, Scope, Expected, N) ->
622634
wait_for_metrics(Authority, Scope, Expected, N - 1)
623635
end.
624636

637+
%% Poll the manager until every connection except Excluded has 0 streams.
638+
wait_streams_released_except(ManagerPid, Excluded) ->
639+
wait_streams_released_except(ManagerPid, Excluded, 100).
640+
641+
wait_streams_released_except(_ManagerPid, _Excluded, 0) ->
642+
{error, timeout};
643+
wait_streams_released_except(ManagerPid, Excluded, N) ->
644+
{_, #{lookup := #{stream_counts := StreamCounts}}} = gun_pool:info(ManagerPid),
645+
Released = lists:all(fun({ConnPid, Count}) ->
646+
ConnPid =:= Excluded orelse Count =:= 0
647+
end, maps:to_list(StreamCounts)),
648+
case Released of
649+
true ->
650+
ok;
651+
false ->
652+
timer:sleep(10),
653+
wait_streams_released_except(ManagerPid, Excluded, N - 1)
654+
end.
655+
625656
%% Poll the manager until every connection's stream count is back to 0.
626657
wait_all_streams_released(ManagerPid) ->
627658
wait_all_streams_released(ManagerPid, 100).

0 commit comments

Comments
 (0)