diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/handlers/read_body_h.erl | 15 | ||||
-rw-r--r-- | test/handlers/ws_ignore.erl | 20 | ||||
-rw-r--r-- | test/http_perf_SUITE.erl | 95 | ||||
-rw-r--r-- | test/ws_perf_SUITE.erl | 134 |
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. |