aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2013-03-30 13:04:24 +0100
committerLoïc Hoguin <[email protected]>2013-03-31 23:08:02 +0200
commit33db3b0d1aafcfbc9aadbad622a4014c021ef10c (patch)
treea14b3c498fbd4cc884c96dfb4d350b4310379f96 /test
parent809a12fdbe5ce355b06bcf197008e1b4e8ea9e21 (diff)
downloadranch-33db3b0d1aafcfbc9aadbad622a4014c021ef10c.tar.gz
ranch-33db3b0d1aafcfbc9aadbad622a4014c021ef10c.tar.bz2
ranch-33db3b0d1aafcfbc9aadbad622a4014c021ef10c.zip
Use a custom supervisor for ranch_conns_sup
This change was designed so that we don't have this supervisor and ranch_listener performing the same job, namely monitoring connection processes, the first through links and the second through monitors. This change also makes possible various optimizations: * Acceptors don't need to know about options, maximum number of connections, or anything else. They can just accept, pass the socket to the supervisor, and when the supervisor replies continue accepting connections. * The supervisor holds most of the information that will be passed to created processes. This reduces copying. * The supervisor temporarily takes ownership of the socket, then creates the connection process and gives it ownership, streamlining the creation. * The supervisor can hold acceptors in their receive loop if max_connections is reached. When this number gets below the limit it can then send a message to a sleeping acceptor to make it resume its operations. * Because we know that all connection process creations are made from the local Erlang node, we can greatly reduce the number operations to be made when calling the supervisor. * Because all acceptors die if this supervisor dies, we can remove even more operations from the calling code. We do not need to monitor or wait for a timeout. This reduces the call code to two statements: send and receive. (Thanks James Fish for helping with that.) * The supervisor only needs to keep track of a list of pids. There is no children specification to be maintained, we do not need to handle restart strategy (no process can be restarted because the socket dies with it). We are using the process dictionary for storing the pids as it proved to be the simplest and fastest solution. * The supervisor maintains a count of current connections, but also of processes (including the ones that removed themselves from the pool), making any query of these values very fast. The supervisor should still be compatible with OTP principles. It responds to calls from the supervisor module as expected, although some of them are disabled and an error will be returned, for example supervisor:start_child/2. It is also started with proc_lib and handles system messages. sys:get_status/1 can thus be used as expected. We can see a great increase in the number of requests/s, a great improvement in the latency of requests, and we can simply accept requests faster than before. It will probably have a bigger increase under virtualized environments, although that's only a guess. As a result of this, we don't write much anymore in the ranch_server ets table, so the write_concurrency option was removed. Tests were also slightly improved to prevent race conditions.
Diffstat (limited to 'test')
-rw-r--r--test/acceptor_SUITE.erl242
1 files changed, 136 insertions, 106 deletions
diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl
index 8a8979f..d6084fd 100644
--- a/test/acceptor_SUITE.erl
+++ b/test/acceptor_SUITE.erl
@@ -111,201 +111,228 @@ misc_bad_transport(_) ->
%% ssl.
ssl_accept_error(Config) ->
- {ok, _} = ranch:start_listener(ssl_accept_error, 1,
+ Name = ssl_accept_error,
+ {ok, ListenerSup} = ranch:start_listener(Name, 1,
ranch_ssl, [{port, 0},
{certfile, ?config(data_dir, Config) ++ "cert.pem"}],
echo_protocol, []),
- Port = ranch:get_port(ssl_accept_error),
- [AcceptorPid] = ets:lookup_element(ranch_server,
- {acceptors, ssl_accept_error}, 2),
+ Port = ranch:get_port(Name),
+ ListenerSupChildren = supervisor:which_children(ListenerSup),
+ {_, AcceptorsSup, _, _}
+ = lists:keyfind(ranch_acceptors_sup, 1, ListenerSupChildren),
+ [{{acceptor, _, _}, AcceptorPid, _, _}]
+ = supervisor:which_children(AcceptorsSup),
true = is_process_alive(AcceptorPid),
{ok, Socket} = gen_tcp:connect("localhost", Port,
[binary, {active, false}, {packet, raw}]),
ok = gen_tcp:close(Socket),
receive after 500 -> ok end,
- true = is_process_alive(AcceptorPid).
+ true = is_process_alive(AcceptorPid),
+ ranch:stop_listener(Name).
ssl_accept_socket(Config) ->
%%% XXX we can't do the spawn to test the controlling process change
%%% because of the bug in ssl
+ Name = ssl_accept_socket,
{ok, S} = ssl:listen(0,
[{certfile, ?config(data_dir, Config) ++ "cert.pem"}, binary,
{active, false}, {packet, raw}, {reuseaddr, true}]),
- {ok, _} = ranch:start_listener(ssl_accept_socket, 1,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_ssl, [{socket, S}], echo_protocol, []),
- Port = ranch:get_port(ssl_accept_socket),
+ Port = ranch:get_port(Name),
{ok, Socket} = ssl:connect("localhost", Port,
[binary, {active, false}, {packet, raw},
{certfile, ?config(data_dir, Config) ++ "cert.pem"}]),
ok = ssl:send(Socket, <<"TCP Ranch is working!">>),
{ok, <<"TCP Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
- ok = ranch:stop_listener(ssl_accept_socket),
+ ok = ranch:stop_listener(Name),
{error, closed} = ssl:recv(Socket, 0, 1000),
%% Make sure the listener stopped.
- {'EXIT', _} = begin catch ranch:get_port(ssl_accept_socket) end,
+ {'EXIT', _} = begin catch ranch:get_port(Name) end,
ok.
ssl_active_echo(Config) ->
- {ok, _} = ranch:start_listener(ssl_active_echo, 1,
+ Name = ssl_active_echo,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_ssl, [{port, 0},
{certfile, ?config(data_dir, Config) ++ "cert.pem"}],
active_echo_protocol, []),
- Port = ranch:get_port(ssl_active_echo),
+ Port = ranch:get_port(Name),
{ok, Socket} = ssl:connect("localhost", Port,
[binary, {active, false}, {packet, raw},
{certfile, ?config(data_dir, Config) ++ "cert.pem"}]),
ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
{ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
- ok = ranch:stop_listener(ssl_active_echo),
+ ok = ranch:stop_listener(Name),
{error, closed} = ssl:recv(Socket, 0, 1000),
%% Make sure the listener stopped.
- {'EXIT', _} = begin catch ranch:get_port(ssl_active_echo) end,
+ {'EXIT', _} = begin catch ranch:get_port(Name) end,
ok.
ssl_echo(Config) ->
- {ok, _} = ranch:start_listener(ssl_echo, 1,
+ Name = ssl_echo,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_ssl, [{port, 0},
{certfile, ?config(data_dir, Config) ++ "cert.pem"}],
echo_protocol, []),
- Port = ranch:get_port(ssl_echo),
+ Port = ranch:get_port(Name),
{ok, Socket} = ssl:connect("localhost", Port,
[binary, {active, false}, {packet, raw},
{certfile, ?config(data_dir, Config) ++ "cert.pem"}]),
ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
{ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
- ok = ranch:stop_listener(ssl_echo),
+ ok = ranch:stop_listener(Name),
{error, closed} = ssl:recv(Socket, 0, 1000),
%% Make sure the listener stopped.
- {'EXIT', _} = begin catch ranch:get_port(ssl_echo) end,
+ {'EXIT', _} = begin catch ranch:get_port(Name) end,
ok.
%% tcp.
tcp_accept_socket(_) ->
+ Name = tcp_accept_socket,
Ref = make_ref(),
Parent = self(),
spawn(fun() ->
- {ok, S} = gen_tcp:listen(0, [binary, {active, false}, {packet, raw},
- {reuseaddr, true}]),
- {ok, _} = ranch:start_listener(tcp_accept_socket, 1,
- ranch_tcp, [{socket, S}], echo_protocol, []),
- Parent ! Ref
- end),
+ {ok, S} = gen_tcp:listen(0, [binary, {active, false}, {packet, raw},
+ {reuseaddr, true}]),
+ {ok, _} = ranch:start_listener(Name, 1,
+ ranch_tcp, [{socket, S}], echo_protocol, []),
+ Parent ! Ref
+ end),
receive
Ref -> ok
end,
-
- Port = ranch:get_port(tcp_accept_socket),
+ Port = ranch:get_port(Name),
{ok, Socket} = gen_tcp:connect("localhost", Port,
[binary, {active, false}, {packet, raw}]),
ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
{ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
- ok = ranch:stop_listener(tcp_accept_socket),
+ ok = ranch:stop_listener(Name),
{error, closed} = gen_tcp:recv(Socket, 0, 1000),
%% Make sure the listener stopped.
- {'EXIT', _} = begin catch ranch:get_port(tcp_accept_socket) end,
+ {'EXIT', _} = begin catch ranch:get_port(Name) end,
ok.
tcp_active_echo(_) ->
- {ok, _} = ranch:start_listener(tcp_active_echo, 1,
+ Name = tcp_active_echo,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}], active_echo_protocol, []),
- Port = ranch:get_port(tcp_active_echo),
+ Port = ranch:get_port(Name),
{ok, Socket} = gen_tcp:connect("localhost", Port,
[binary, {active, false}, {packet, raw}]),
ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
{ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
- ok = ranch:stop_listener(tcp_active_echo),
+ ok = ranch:stop_listener(Name),
{error, closed} = gen_tcp:recv(Socket, 0, 1000),
%% Make sure the listener stopped.
- {'EXIT', _} = begin catch ranch:get_port(tcp_active_echo) end,
+ {'EXIT', _} = begin catch ranch:get_port(Name) end,
ok.
tcp_echo(_) ->
- {ok, _} = ranch:start_listener(tcp_echo, 1,
+ Name = tcp_echo,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}], echo_protocol, []),
- Port = ranch:get_port(tcp_echo),
+ Port = ranch:get_port(Name),
{ok, Socket} = gen_tcp:connect("localhost", Port,
[binary, {active, false}, {packet, raw}]),
ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
{ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
- ok = ranch:stop_listener(tcp_echo),
+ ok = ranch:stop_listener(Name),
{error, closed} = gen_tcp:recv(Socket, 0, 1000),
%% Make sure the listener stopped.
- {'EXIT', _} = begin catch ranch:get_port(tcp_echo) end,
+ {'EXIT', _} = begin catch ranch:get_port(Name) end,
ok.
tcp_max_connections(_) ->
- {ok, _} = ranch:start_listener(tcp_max_connections, 1,
+ Name = tcp_max_connections,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}, {max_connections, 10}],
notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
- Port = ranch:get_port(tcp_max_connections),
- %% @todo We'll probably want a more direct interface to count_connections.
- ListenerPid = ranch_server:lookup_listener(tcp_max_connections),
+ Port = ranch:get_port(Name),
ok = connect_loop(Port, 11, 150),
- 10 = ranch_server:count_connections(ListenerPid),
+ 10 = ranch_server:count_connections(Name),
10 = receive_loop(connected, 400),
- 1 = receive_loop(connected, 1000).
+ 1 = receive_loop(connected, 1000),
+ ranch:stop_listener(Name).
tcp_max_connections_and_beyond(_) ->
- {ok, _} = ranch:start_listener(tcp_max_connections_and_beyond, 1,
+ Name = tcp_max_connections_and_beyond,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}, {max_connections, 10}],
remove_conn_and_wait_protocol, [{remove, true}]),
- Port = ranch:get_port(tcp_max_connections_and_beyond),
- %% @todo We'll probably want a more direct interface to count_connections.
- ListenerPid = ranch_server:lookup_listener(tcp_max_connections_and_beyond),
+ Port = ranch:get_port(Name),
ok = connect_loop(Port, 10, 0),
- 0 = ranch_server:count_connections(ListenerPid),
- ranch:set_protocol_options(tcp_max_connections_and_beyond,
- [{remove, false}]),
- receive after 500 -> ok end,
+ receive after 250 -> ok end,
+ 0 = ranch_server:count_connections(Name),
+ 10 = length(supervisor:which_children(
+ ranch_server:lookup_connections_sup(Name))),
+ Counts = supervisor:count_children(
+ ranch_server:lookup_connections_sup(Name)),
+ {_, 1} = lists:keyfind(specs, 1, Counts),
+ {_, 0} = lists:keyfind(supervisors, 1, Counts),
+ {_, 10} = lists:keyfind(active, 1, Counts),
+ {_, 10} = lists:keyfind(workers, 1, Counts),
+ ranch:set_protocol_options(Name, [{remove, false}]),
+ receive after 250 -> ok end,
ok = connect_loop(Port, 10, 0),
- receive after 500 -> ok end,
- 10 = ranch_server:count_connections(ListenerPid).
+ receive after 250 -> ok end,
+ 10 = ranch_server:count_connections(Name),
+ 20 = length(supervisor:which_children(
+ ranch_server:lookup_connections_sup(Name))),
+ Counts2 = supervisor:count_children(
+ ranch_server:lookup_connections_sup(Name)),
+ {_, 20} = lists:keyfind(active, 1, Counts2),
+ {_, 20} = lists:keyfind(workers, 1, Counts2),
+ ranch:stop_listener(Name).
tcp_set_max_connections(_) ->
- {ok, _} = ranch:start_listener(tcp_set_max_connections, 1,
+ Name = tcp_set_max_connections,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}, {max_connections, 10}],
notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
- Port = ranch:get_port(tcp_set_max_connections),
- %% @todo We'll probably want a more direct interface to count_connections.
- ListenerPid = ranch_server:lookup_listener(tcp_set_max_connections),
+ Port = ranch:get_port(Name),
ok = connect_loop(Port, 20, 0),
- 10 = ranch_server:count_connections(ListenerPid),
+ 10 = ranch_server:count_connections(Name),
10 = receive_loop(connected, 1000),
- 10 = ranch:get_max_connections(tcp_set_max_connections),
- ranch:set_max_connections(tcp_set_max_connections, 20),
+ 10 = ranch:get_max_connections(Name),
+ ranch:set_max_connections(Name, 20),
10 = receive_loop(connected, 1000),
- 20 = ranch:get_max_connections(tcp_set_max_connections).
+ 20 = ranch:get_max_connections(Name),
+ ranch:stop_listener(Name).
tcp_infinity_max_connections(_) ->
- {ok, _} = ranch:start_listener(tcp_infinity_max_connections, 1,
+ Name = tcp_infinity_max_connections,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}, {max_connections, 10}],
notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
- Port = ranch:get_port(tcp_infinity_max_connections),
- %% @todo We'll probably want a more direct interface to count_connections.
- ListenerPid = ranch_server:lookup_listener(tcp_infinity_max_connections),
+ Port = ranch:get_port(Name),
ok = connect_loop(Port, 20, 0),
- 10 = ranch_server:count_connections(ListenerPid),
+ 10 = ranch_server:count_connections(Name),
10 = receive_loop(connected, 1000),
- 10 = ranch:get_max_connections(tcp_infinity_max_connections),
- ranch:set_max_connections(tcp_infinity_max_connections, infinity),
- 0 = ranch_server:count_connections(ListenerPid),
- infinity = ranch:get_max_connections(tcp_infinity_max_connections),
- ranch:set_max_connections(tcp_infinity_max_connections, 10),
- 0 = ranch_server:count_connections(ListenerPid),
+ 10 = ranch_server:count_connections(Name),
+ 10 = ranch:get_max_connections(Name),
+ ranch:set_max_connections(Name, infinity),
+ receive after 250 -> ok end,
+ 20 = ranch_server:count_connections(Name),
+ infinity = ranch:get_max_connections(Name),
+ ranch:set_max_connections(Name, 10),
+ 20 = ranch_server:count_connections(Name),
10 = receive_loop(connected, 1000),
- 10 = ranch_server:count_connections(ListenerPid). % count could be off
+ ranch:stop_listener(Name).
tcp_upgrade(_) ->
- {ok, _} = ranch:start_listener(tcp_upgrade, 1,
+ Name = tcp_upgrade,
+ {ok, _} = ranch:start_listener(Name, 1,
ranch_tcp, [{port, 0}],
notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
- Port = ranch:get_port(tcp_upgrade),
+ Port = ranch:get_port(Name),
ok = connect_loop(Port, 1, 0),
receive connected -> ok after 1000 -> error(timeout) end,
- ranch:set_protocol_options(tcp_upgrade, [{msg, upgraded}, {pid, self()}]),
+ ranch:set_protocol_options(Name, [{msg, upgraded}, {pid, self()}]),
ok = connect_loop(Port, 1, 0),
- receive upgraded -> ok after 1000 -> error(timeout) end.
+ receive upgraded -> ok after 1000 -> error(timeout) end,
+ ranch:stop_listener(Name).
%% Supervisor tests
@@ -313,13 +340,13 @@ supervisor_clean_restart(_) ->
%% There we verify that mature listener death will not let
%% whole supervisor down and also the supervisor itself will
%% restart everything properly.
- Ref = supervisor_clean_restart,
+ Name = supervisor_clean_restart,
NbAcc = 4,
- {ok, Pid} = ranch:start_listener(Ref,
+ {ok, Pid} = ranch:start_listener(Name,
NbAcc, ranch_tcp, [{port, 0}], echo_protocol, []),
%% Trace supervisor spawns.
1 = erlang:trace(Pid, true, [procs, set_on_spawn]),
- ListenerPid0 = ranch_server:lookup_listener(Ref),
+ ListenerPid0 = ranch_server:lookup_listener(Name),
erlang:exit(ListenerPid0, kill),
receive after 1000 -> ok end,
%% Verify that supervisor is alive
@@ -339,22 +366,24 @@ supervisor_clean_restart(_) ->
error(invalid_restart)
after 1000 -> ok end,
%% Verify that new children registered themselves properly.
- ListenerPid = ranch_server:lookup_listener(Ref),
+ ListenerPid = ranch_server:lookup_listener(Name),
_ = erlang:trace(all, false, [all]),
- ok = clean_traces().
+ ok = clean_traces(),
+ ranch:stop_listener(Name).
supervisor_clean_child_restart(_) ->
%% Then we verify that only parts of the supervision tree
%% restarted in the case of failure.
- Ref = supervisor_clean_child_restart,
+ Name = supervisor_clean_child_restart,
%% Trace socket allocations.
_ = erlang:trace(new, true, [call]),
- 1 = erlang:trace_pattern({ranch_tcp, listen, 1}, [{'_', [], [{return_trace}]}], [global]),
- {ok, Pid} = ranch:start_listener(Ref,
+ 1 = erlang:trace_pattern({ranch_tcp, listen, 1},
+ [{'_', [], [{return_trace}]}], [global]),
+ {ok, Pid} = ranch:start_listener(Name,
1, ranch_tcp, [{port, 0}], echo_protocol, []),
%% Trace supervisor spawns.
1 = erlang:trace(Pid, true, [procs, set_on_spawn]),
- ListenerPid0 = ranch_server:lookup_listener(Ref),
+ ListenerPid = ranch_server:lookup_listener(Name),
%% Manually shut the listening socket down.
LSocket = receive
{trace, _, return_from, {ranch_tcp, listen, 1}, {ok, Socket}} ->
@@ -366,31 +395,31 @@ supervisor_clean_child_restart(_) ->
receive after 1000 -> ok end,
%% Verify that supervisor and its first two children are alive.
true = is_process_alive(Pid),
- true = is_process_alive(ListenerPid0),
+ true = is_process_alive(ListenerPid),
%% Check that acceptors_sup is restarted properly.
AccSupPid = receive {trace, Pid, spawn, Pid1, _} -> Pid1 end,
- AccPid = receive {trace, AccSupPid, spawn, Pid2, _} -> Pid2 end,
- receive {trace, AccPid, spawn, _, _} -> ok end,
+ receive {trace, AccSupPid, spawn, _, _} -> ok end,
%% No more traces then.
receive
{trace, _, spawn, _, _} -> error(invalid_restart)
after 1000 -> ok end,
%% Verify that children still registered right.
- ListenerPid0 = ranch_server:lookup_listener(Ref),
+ ListenerPid = ranch_server:lookup_listener(Name),
_ = erlang:trace_pattern({ranch_tcp, listen, 1}, false, []),
_ = erlang:trace(all, false, [all]),
ok = clean_traces(),
- ok.
+ ranch:stop_listener(Name).
supervisor_conns_alive(_) ->
%% And finally we make sure that in the case of partial failure
%% live connections are not being killed.
- Ref = supervisor_conns_alive,
+ Name = supervisor_conns_alive,
_ = erlang:trace(new, true, [call]),
- 1 = erlang:trace_pattern({ranch_tcp, listen, 1}, [{'_', [], [{return_trace}]}], [global]),
- {ok, _} = ranch:start_listener(Ref,
- 1, ranch_tcp, [{port, 0}], remove_conn_and_wait_protocol, [{remove, false}]),
- ok,
+ 1 = erlang:trace_pattern({ranch_tcp, listen, 1},
+ [{'_', [], [{return_trace}]}], [global]),
+ {ok, _} = ranch:start_listener(Name, 1,
+ ranch_tcp, [{port, 0}],
+ remove_conn_and_wait_protocol, [{remove, false}]),
%% Get the listener socket
LSocket = receive
{trace, _, return_from, {ranch_tcp, listen, 1}, {ok, S}} ->
@@ -398,7 +427,7 @@ supervisor_conns_alive(_) ->
after 0 ->
error(lsocket_unknown)
end,
- TcpPort = ranch:get_port(Ref),
+ TcpPort = ranch:get_port(Name),
{ok, Socket} = gen_tcp:connect("localhost", TcpPort,
[binary, {active, true}, {packet, raw}]),
%% Shut the socket down
@@ -408,17 +437,8 @@ supervisor_conns_alive(_) ->
ok = gen_tcp:send(Socket, <<"poke">>),
receive {tcp_closed, _} -> ok end,
_ = erlang:trace(all, false, [all]),
- ok = clean_traces().
-
-clean_traces() ->
- receive
- {trace, _, _, _} ->
- clean_traces();
- {trace, _, _, _, _} ->
- clean_traces()
- after 0 ->
- ok
- end.
+ ok = clean_traces(),
+ ranch:stop_listener(Name).
%% Utility functions.
@@ -438,3 +458,13 @@ receive_loop(Message, Timeout, N) ->
after Timeout ->
N
end.
+
+clean_traces() ->
+ receive
+ {trace, _, _, _} ->
+ clean_traces();
+ {trace, _, _, _, _} ->
+ clean_traces()
+ after 0 ->
+ ok
+ end.