aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/handlers/read_body_h.erl15
-rw-r--r--test/handlers/ws_ignore.erl20
-rw-r--r--test/http_perf_SUITE.erl95
-rw-r--r--test/ws_perf_SUITE.erl134
4 files changed, 211 insertions, 53 deletions
diff --git a/test/handlers/read_body_h.erl b/test/handlers/read_body_h.erl
new file mode 100644
index 0000000..a0de3b3
--- /dev/null
+++ b/test/handlers/read_body_h.erl
@@ -0,0 +1,15 @@
+%% This module reads the request body fully and send a 204 response.
+
+-module(read_body_h).
+
+-export([init/2]).
+
+init(Req0, Opts) ->
+ {ok, Req} = read_body(Req0),
+ {ok, cowboy_req:reply(200, #{}, Req), Opts}.
+
+read_body(Req0) ->
+ case cowboy_req:read_body(Req0) of
+ {ok, _, Req} -> {ok, Req};
+ {more, _, Req} -> read_body(Req)
+ end.
diff --git a/test/handlers/ws_ignore.erl b/test/handlers/ws_ignore.erl
new file mode 100644
index 0000000..9fe3322
--- /dev/null
+++ b/test/handlers/ws_ignore.erl
@@ -0,0 +1,20 @@
+%% Feel free to use, reuse and abuse the code in this file.
+
+-module(ws_ignore).
+
+-export([init/2]).
+-export([websocket_handle/2]).
+-export([websocket_info/2]).
+
+init(Req, _) ->
+ {cowboy_websocket, Req, undefined, #{
+ compress => true
+ }}.
+
+websocket_handle({text, <<"CHECK">>}, State) ->
+ {[{text, <<"CHECK">>}], State};
+websocket_handle(_Frame, State) ->
+ {[], State}.
+
+websocket_info(_Info, State) ->
+ {[], State}.
diff --git a/test/http_perf_SUITE.erl b/test/http_perf_SUITE.erl
index 8702b9d..161f2b5 100644
--- a/test/http_perf_SUITE.erl
+++ b/test/http_perf_SUITE.erl
@@ -32,7 +32,7 @@ groups() ->
init_per_suite(Config) ->
do_log("", []),
%% Optionally enable `perf` for the current node.
-% spawn(fun() -> ct:pal(os:cmd("perf record -g -F 9999 -o /tmp/ws_perf.data -p " ++ os:getpid() ++ " -- sleep 60")) end),
+% spawn(fun() -> ct:pal(os:cmd("perf record -g -F 9999 -o /tmp/http_perf.data -p " ++ os:getpid() ++ " -- sleep 60")) end),
Config.
end_per_suite(_) ->
@@ -43,7 +43,16 @@ init_per_group(Name, Config) ->
%% HTTP/1.1
max_keepalive => infinity,
%% HTTP/2
- max_received_frame_rate => {10_000_000, 1}
+ %% @todo Must configure Gun for performance too.
+ connection_window_margin_size => 64*1024,
+ enable_connect_protocol => true,
+ env => #{dispatch => init_dispatch(Config)},
+ max_frame_size_sent => 64*1024,
+ max_frame_size_received => 16384 * 1024 - 1,
+ max_received_frame_rate => {10_000_000, 1},
+ stream_window_data_threshold => 1024,
+ stream_window_margin_size => 64*1024
+
})].
end_per_group(Name, _) ->
@@ -54,10 +63,19 @@ end_per_group(Name, _) ->
init_dispatch(_) ->
cowboy_router:compile([{'_', [
- {"/", hello_h, []}
+ {"/", hello_h, []},
+ {"/read_body", read_body_h, []}
]}]).
-%% Tests.
+%% Tests: Hello world.
+
+plain_h_hello_1(Config) ->
+ doc("Plain HTTP handler Hello World; 10K requests per 1 client."),
+ do_bench_get(?FUNCTION_NAME, "/", #{}, 1, 10000, Config).
+
+plain_h_hello_10(Config) ->
+ doc("Plain HTTP handler Hello World; 10K requests per 10 clients."),
+ do_bench_get(?FUNCTION_NAME, "/", #{}, 10, 10000, Config).
stream_h_hello_1(Config) ->
doc("Stream handler Hello World; 10K requests per 1 client."),
@@ -81,51 +99,86 @@ do_stream_h_hello(Config, NumClients) ->
do_bench_get(?FUNCTION_NAME, "/", #{}, NumClients, 10000, Config),
ranch:set_protocol_options(Ref, ProtoOpts).
-plain_h_hello_1(Config) ->
- doc("Plain HTTP handler Hello World; 10K requests per 1 client."),
- do_bench_get(?FUNCTION_NAME, "/", #{}, 1, 10000, Config).
+%% Tests: Large body upload.
-plain_h_hello_10(Config) ->
- doc("Plain HTTP handler Hello World; 10K requests per 10 clients."),
- do_bench_get(?FUNCTION_NAME, "/", #{}, 10, 10000, Config).
+plain_h_1M_post_1(Config) ->
+ doc("Plain HTTP handler body reading; 100 requests per 1 client."),
+ do_bench_post(?FUNCTION_NAME, "/read_body", #{}, <<0:8_000_000>>, 1, 10000, Config).
+
+plain_h_1M_post_10(Config) ->
+ doc("Plain HTTP handler body reading; 100 requests per 10 clients."),
+ do_bench_post(?FUNCTION_NAME, "/read_body", #{}, <<0:8_000_000>>, 10, 10000, Config).
%% Internal.
do_bench_get(What, Path, Headers, NumClients, NumRuns, Config) ->
- Clients = [spawn_link(?MODULE, do_bench_proc, [self(), What, Path, Headers, NumRuns, Config])
+ Clients = [spawn_link(?MODULE, do_bench_get_proc,
+ [self(), What, Path, Headers, NumRuns, Config])
|| _ <- lists:seq(1, NumClients)],
_ = [receive {What, ready} -> ok end || _ <- Clients],
- {Time, _} = timer:tc(?MODULE, do_bench_get1, [What, Clients]),
+ {Time, _} = timer:tc(?MODULE, do_bench_wait, [What, Clients]),
do_log("~32s: ~8bµs ~8.1freqs/s", [
[atom_to_list(config(group, Config)), $., atom_to_list(What)],
Time,
(NumClients * NumRuns) / Time * 1_000_000]),
ok.
-do_bench_get1(What, Clients) ->
- _ = [ClientPid ! {What, go} || ClientPid <- Clients],
- _ = [receive {What, done} -> ok end || _ <- Clients],
+do_bench_get_proc(Parent, What, Path, Headers0, NumRuns, Config) ->
+ ConnPid = gun_open(Config),
+ Headers = Headers0#{<<"accept-encoding">> => <<"gzip">>},
+ Parent ! {What, ready},
+ receive {What, go} -> ok end,
+ do_bench_get_run(ConnPid, Path, Headers, NumRuns),
+ Parent ! {What, done},
+ gun:close(ConnPid).
+
+do_bench_get_run(_, _, _, 0) ->
+ ok;
+do_bench_get_run(ConnPid, Path, Headers, Num) ->
+ Ref = gun:request(ConnPid, <<"GET">>, Path, Headers, <<>>),
+ {response, IsFin, 200, _RespHeaders} = gun:await(ConnPid, Ref, infinity),
+ {ok, _} = case IsFin of
+ nofin -> gun:await_body(ConnPid, Ref, infinity);
+ fin -> {ok, <<>>}
+ end,
+ do_bench_get_run(ConnPid, Path, Headers, Num - 1).
+
+do_bench_post(What, Path, Headers, Body, NumClients, NumRuns, Config) ->
+ Clients = [spawn_link(?MODULE, do_bench_post_proc,
+ [self(), What, Path, Headers, Body, NumRuns, Config])
+ || _ <- lists:seq(1, NumClients)],
+ _ = [receive {What, ready} -> ok end || _ <- Clients],
+ {Time, _} = timer:tc(?MODULE, do_bench_wait, [What, Clients]),
+ do_log("~32s: ~8bµs ~8.1freqs/s", [
+ [atom_to_list(config(group, Config)), $., atom_to_list(What)],
+ Time,
+ (NumClients * NumRuns) / Time * 1_000_000]),
ok.
-do_bench_proc(Parent, What, Path, Headers0, NumRuns, Config) ->
+do_bench_post_proc(Parent, What, Path, Headers0, Body, NumRuns, Config) ->
ConnPid = gun_open(Config),
Headers = Headers0#{<<"accept-encoding">> => <<"gzip">>},
Parent ! {What, ready},
receive {What, go} -> ok end,
- do_bench_run(ConnPid, Path, Headers, NumRuns),
+ do_bench_post_run(ConnPid, Path, Headers, Body, NumRuns),
Parent ! {What, done},
gun:close(ConnPid).
-do_bench_run(_, _, _, 0) ->
+do_bench_post_run(_, _, _, _, 0) ->
ok;
-do_bench_run(ConnPid, Path, Headers, Num) ->
- Ref = gun:request(ConnPid, <<"GET">>, Path, Headers, <<>>),
+do_bench_post_run(ConnPid, Path, Headers, Body, Num) ->
+ Ref = gun:request(ConnPid, <<"POST">>, Path, Headers, Body),
{response, IsFin, 200, _RespHeaders} = gun:await(ConnPid, Ref, infinity),
{ok, _} = case IsFin of
nofin -> gun:await_body(ConnPid, Ref, infinity);
fin -> {ok, <<>>}
end,
- do_bench_run(ConnPid, Path, Headers, Num - 1).
+ do_bench_post_run(ConnPid, Path, Headers, Body, Num - 1).
+
+do_bench_wait(What, Clients) ->
+ _ = [ClientPid ! {What, go} || ClientPid <- Clients],
+ _ = [receive {What, done} -> ok end || _ <- Clients],
+ ok.
do_log(Str, Args) ->
ct:log(Str, Args),
diff --git a/test/ws_perf_SUITE.erl b/test/ws_perf_SUITE.erl
index 2fd2db2..2d7a1c0 100644
--- a/test/ws_perf_SUITE.erl
+++ b/test/ws_perf_SUITE.erl
@@ -60,6 +60,7 @@ init_per_group(Name, Config) when Name =:= h2c; Name =:= h2c_compress ->
env => #{dispatch => init_dispatch(Config)},
max_frame_size_sent => 64*1024,
max_frame_size_received => 16384 * 1024 - 1,
+ max_received_frame_rate => {10_000_000, 1},
stream_window_data_threshold => 1024,
stream_window_margin_size => 64*1024
}, [{flavor, Flavor}|Config]),
@@ -102,13 +103,14 @@ end_per_group(Name, _Config) ->
init_dispatch(_Config) ->
cowboy_router:compile([
{"localhost", [
- {"/ws_echo", ws_echo, []}
+ {"/ws_echo", ws_echo, []},
+ {"/ws_ignore", ws_ignore, []}
]}
]).
%% Support functions for testing using Gun.
-do_gun_open_ws(Config) ->
+do_gun_open_ws(Path, Config) ->
ConnPid = gun_open(Config, #{
http2_opts => #{
connection_window_margin_size => 64*1024,
@@ -127,7 +129,7 @@ do_gun_open_ws(Config) ->
{notify, settings_changed, #{enable_connect_protocol := true}}
= gun:await(ConnPid, undefined) %% @todo Maybe have a gun:await/1?
end,
- StreamRef = gun:ws_upgrade(ConnPid, "/ws_echo"),
+ StreamRef = gun:ws_upgrade(ConnPid, Path),
receive
{gun_upgrade, ConnPid, StreamRef, [<<"websocket">>], _} ->
{ok, ConnPid, StreamRef};
@@ -149,72 +151,140 @@ receive_ws(ConnPid, StreamRef) ->
%% Tests.
-one_00064KiB(Config) ->
+echo_1_00064KiB(Config) ->
doc("Send and receive a 64KiB frame."),
- do_full(Config, one, 1, 64 * 1024).
+ do_echo(Config, echo_1, 1, 64 * 1024).
-one_00256KiB(Config) ->
+echo_1_00256KiB(Config) ->
doc("Send and receive a 256KiB frame."),
- do_full(Config, one, 1, 256 * 1024).
+ do_echo(Config, echo_1, 1, 256 * 1024).
-one_01024KiB(Config) ->
+echo_1_01024KiB(Config) ->
doc("Send and receive a 1024KiB frame."),
- do_full(Config, one, 1, 1024 * 1024).
+ do_echo(Config, echo_1, 1, 1024 * 1024).
-one_04096KiB(Config) ->
+echo_1_04096KiB(Config) ->
doc("Send and receive a 4096KiB frame."),
- do_full(Config, one, 1, 4096 * 1024).
+ do_echo(Config, echo_1, 1, 4096 * 1024).
%% Minus one because frames can only get so big.
-one_16384KiB(Config) ->
+echo_1_16384KiB(Config) ->
doc("Send and receive a 16384KiB - 1 frame."),
- do_full(Config, one, 1, 16384 * 1024 - 1).
+ do_echo(Config, echo_1, 1, 16384 * 1024 - 1).
-repeat_00000B(Config) ->
+echo_N_00000B(Config) ->
doc("Send and receive a 0B frame 1000 times."),
- do_full(Config, repeat, 1000, 0).
+ do_echo(Config, echo_N, 1000, 0).
-repeat_00256B(Config) ->
+echo_N_00256B(Config) ->
doc("Send and receive a 256B frame 1000 times."),
- do_full(Config, repeat, 1000, 256).
+ do_echo(Config, echo_N, 1000, 256).
-repeat_01024B(Config) ->
+echo_N_01024B(Config) ->
doc("Send and receive a 1024B frame 1000 times."),
- do_full(Config, repeat, 1000, 1024).
+ do_echo(Config, echo_N, 1000, 1024).
-repeat_04096B(Config) ->
+echo_N_04096B(Config) ->
doc("Send and receive a 4096B frame 1000 times."),
- do_full(Config, repeat, 1000, 4096).
+ do_echo(Config, echo_N, 1000, 4096).
-repeat_16384B(Config) ->
+echo_N_16384B(Config) ->
doc("Send and receive a 16384B frame 1000 times."),
- do_full(Config, repeat, 1000, 16384).
+ do_echo(Config, echo_N, 1000, 16384).
-%repeat_16384B_10K(Config) ->
+%echo_N_16384B_10K(Config) ->
% doc("Send and receive a 16384B frame 10000 times."),
-% do_full(Config, repeat, 10000, 16384).
+% do_echo(Config, echo_N, 10000, 16384).
-do_full(Config, What, Num, FrameSize) ->
- {ok, ConnPid, StreamRef} = do_gun_open_ws(Config),
+do_echo(Config, What, Num, FrameSize) ->
+ {ok, ConnPid, StreamRef} = do_gun_open_ws("/ws_echo", Config),
FrameType = config(frame_type, Config),
FrameData = case FrameType of
text -> do_text_data(Config, FrameSize);
binary -> rand:bytes(FrameSize)
end,
%% Heat up the processes before doing the real run.
-% do_full1(ConnPid, StreamRef, Num, FrameType, FrameData),
- {Time, _} = timer:tc(?MODULE, do_full1, [ConnPid, StreamRef, Num, FrameType, FrameData]),
+% do_echo_loop(ConnPid, StreamRef, Num, FrameType, FrameData),
+ {Time, _} = timer:tc(?MODULE, do_echo_loop, [ConnPid, StreamRef, Num, FrameType, FrameData]),
do_log("~-6s ~-6s ~6s: ~8bµs", [What, FrameType, do_format_size(FrameSize), Time]),
gun:ws_send(ConnPid, StreamRef, close),
{ok, close} = receive_ws(ConnPid, StreamRef),
gun_down(ConnPid).
-do_full1(_, _, 0, _, _) ->
+do_echo_loop(_, _, 0, _, _) ->
ok;
-do_full1(ConnPid, StreamRef, Num, FrameType, FrameData) ->
+do_echo_loop(ConnPid, StreamRef, Num, FrameType, FrameData) ->
gun:ws_send(ConnPid, StreamRef, {FrameType, FrameData}),
{ok, {FrameType, FrameData}} = receive_ws(ConnPid, StreamRef),
- do_full1(ConnPid, StreamRef, Num - 1, FrameType, FrameData).
+ do_echo_loop(ConnPid, StreamRef, Num - 1, FrameType, FrameData).
+
+send_1_00064KiB(Config) ->
+ doc("Send a 64KiB frame."),
+ do_send(Config, send_1, 1, 64 * 1024).
+
+send_1_00256KiB(Config) ->
+ doc("Send a 256KiB frame."),
+ do_send(Config, send_1, 1, 256 * 1024).
+
+send_1_01024KiB(Config) ->
+ doc("Send a 1024KiB frame."),
+ do_send(Config, send_1, 1, 1024 * 1024).
+
+send_1_04096KiB(Config) ->
+ doc("Send a 4096KiB frame."),
+ do_send(Config, send_1, 1, 4096 * 1024).
+
+%% Minus one because frames can only get so big.
+send_1_16384KiB(Config) ->
+ doc("Send a 16384KiB - 1 frame."),
+ do_send(Config, send_1, 1, 16384 * 1024 - 1).
+
+send_N_00000B(Config) ->
+ doc("Send a 0B frame 10000 times."),
+ do_send(Config, send_N, 10000, 0).
+
+send_N_00256B(Config) ->
+ doc("Send a 256B frame 10000 times."),
+ do_send(Config, send_N, 10000, 256).
+
+send_N_01024B(Config) ->
+ doc("Send a 1024B frame 10000 times."),
+ do_send(Config, send_N, 10000, 1024).
+
+send_N_04096B(Config) ->
+ doc("Send a 4096B frame 10000 times."),
+ do_send(Config, send_N, 10000, 4096).
+
+send_N_16384B(Config) ->
+ doc("Send a 16384B frame 10000 times."),
+ do_send(Config, send_N, 10000, 16384).
+
+%send_N_16384B_10K(Config) ->
+% doc("Send and receive a 16384B frame 10000 times."),
+% do_send(Config, send_N, 10000, 16384).
+
+do_send(Config, What, Num, FrameSize) ->
+ {ok, ConnPid, StreamRef} = do_gun_open_ws("/ws_ignore", Config),
+ FrameType = config(frame_type, Config),
+ FrameData = case FrameType of
+ text -> do_text_data(Config, FrameSize);
+ binary -> rand:bytes(FrameSize)
+ end,
+ %% Heat up the processes before doing the real run.
+% do_send_loop(ConnPid, StreamRef, Num, FrameType, FrameData),
+ {Time, _} = timer:tc(?MODULE, do_send_loop, [ConnPid, StreamRef, Num, FrameType, FrameData]),
+ do_log("~-6s ~-6s ~6s: ~8bµs", [What, FrameType, do_format_size(FrameSize), Time]),
+ gun:ws_send(ConnPid, StreamRef, close),
+ {ok, close} = receive_ws(ConnPid, StreamRef),
+ gun_down(ConnPid).
+
+do_send_loop(ConnPid, StreamRef, 0, _, _) ->
+ gun:ws_send(ConnPid, StreamRef, {text, <<"CHECK">>}),
+ {ok, {text, <<"CHECK">>}} = receive_ws(ConnPid, StreamRef),
+ ok;
+do_send_loop(ConnPid, StreamRef, Num, FrameType, FrameData) ->
+ gun:ws_send(ConnPid, StreamRef, {FrameType, FrameData}),
+ do_send_loop(ConnPid, StreamRef, Num - 1, FrameType, FrameData).
%% Internal.