TCP/TLS connection pooling for Eio

reset

Changed files
+334 -196
test
+17 -1
test/dune
···
(executable
(name stress_test)
-
(libraries conpool eio eio_main logs logs.fmt fmt))
+
(modules stress_test trace)
+
(libraries conpool eio eio_main unix))
+
+
(executable
+
(name visualize)
+
(modules visualize)
+
(libraries str))
+
+
(rule
+
(alias runtest)
+
(deps stress_test.exe)
+
(action (run ./stress_test.exe --all -o stress_test_results.json)))
+
+
(rule
+
(alias runtest)
+
(deps visualize.exe stress_test_results.json)
+
(action (run ./visualize.exe -i stress_test_results.json -o stress_test_results.html)))
+317 -195
test/stress_test.ml
···
Spawns variable number of echo servers on random ports, then exercises
the connection pool with multiple parallel client fibers.
+
Collects detailed event traces for visualization.
*)
-
-
let src = Logs.Src.create "stress_test" ~doc:"Connection pool stress test"
-
module Log = (val Logs.src_log src : Logs.LOG)
(** Configuration for the stress test *)
type config = {
+
name : string; (** Test name for identification *)
num_servers : int; (** Number of echo servers to spawn *)
num_clients : int; (** Number of client connections per server *)
messages_per_client : int; (** Number of messages each client sends *)
···
}
let default_config = {
+
name = "default";
num_servers = 3;
num_clients = 10;
messages_per_client = 5;
···
pool_size = 5;
}
+
(** Test presets for different scenarios *)
+
let presets = [
+
(* High connection reuse - few connections, many messages *)
+
{ name = "high_reuse";
+
num_servers = 2;
+
num_clients = 20;
+
messages_per_client = 50;
+
max_parallel_clients = 10;
+
message_size = 32;
+
pool_size = 3;
+
};
+
(* Many endpoints - test endpoint scaling *)
+
{ name = "many_endpoints";
+
num_servers = 10;
+
num_clients = 10;
+
messages_per_client = 10;
+
max_parallel_clients = 50;
+
message_size = 64;
+
pool_size = 5;
+
};
+
(* High concurrency - stress parallel connections *)
+
{ name = "high_concurrency";
+
num_servers = 3;
+
num_clients = 100;
+
messages_per_client = 5;
+
max_parallel_clients = 100;
+
message_size = 64;
+
pool_size = 20;
+
};
+
(* Large messages - test throughput *)
+
{ name = "large_messages";
+
num_servers = 3;
+
num_clients = 20;
+
messages_per_client = 20;
+
max_parallel_clients = 30;
+
message_size = 1024;
+
pool_size = 10;
+
};
+
(* Constrained pool - force queuing *)
+
{ name = "constrained_pool";
+
num_servers = 2;
+
num_clients = 50;
+
messages_per_client = 10;
+
max_parallel_clients = 50;
+
message_size = 64;
+
pool_size = 2;
+
};
+
(* Burst traffic - many clients, few messages each *)
+
{ name = "burst_traffic";
+
num_servers = 5;
+
num_clients = 200;
+
messages_per_client = 2;
+
max_parallel_clients = 100;
+
message_size = 32;
+
pool_size = 15;
+
};
+
]
+
+
(** Extended stress test - 100x messages, 10x clients/servers *)
+
let extended_preset = {
+
name = "extended_stress";
+
num_servers = 30;
+
num_clients = 1000;
+
messages_per_client = 100;
+
max_parallel_clients = 500;
+
message_size = 128;
+
pool_size = 50;
+
}
+
(** Statistics collected during test *)
-
type stats = {
-
mutable total_connections : int;
-
mutable total_messages : int;
-
mutable total_bytes : int;
-
mutable errors : int;
-
mutable min_latency : float;
-
mutable max_latency : float;
-
mutable total_latency : float;
+
type latency_stats = {
+
mutable count : int;
+
mutable total : float;
+
mutable min : float;
+
mutable max : float;
}
-
let create_stats () = {
-
total_connections = 0;
-
total_messages = 0;
-
total_bytes = 0;
-
errors = 0;
-
min_latency = Float.infinity;
-
max_latency = 0.0;
-
total_latency = 0.0;
+
let create_latency_stats () = {
+
count = 0;
+
total = 0.0;
+
min = Float.infinity;
+
max = 0.0;
}
let update_latency stats latency =
-
stats.min_latency <- min stats.min_latency latency;
-
stats.max_latency <- max stats.max_latency latency;
-
stats.total_latency <- stats.total_latency +. latency
+
stats.count <- stats.count + 1;
+
stats.total <- stats.total +. latency;
+
stats.min <- min stats.min latency;
+
stats.max <- max stats.max latency
(** Generate a random message of given size *)
let generate_message size =
···
String.init size (fun _ -> chars.[Random.int len])
(** Echo server handler - echoes back everything it receives *)
-
let handle_echo_client flow addr =
-
Log.debug (fun m -> m "Echo server: accepted connection from %a"
-
Eio.Net.Sockaddr.pp addr);
+
let handle_echo_client flow _addr =
let buf = Cstruct.create 4096 in
let rec loop () =
match Eio.Flow.single_read flow buf with
···
let data = Cstruct.sub buf 0 n in
Eio.Flow.write flow [data];
loop ()
-
| exception End_of_file ->
-
Log.debug (fun m -> m "Echo server: client disconnected from %a"
-
Eio.Net.Sockaddr.pp addr)
+
| exception End_of_file -> ()
in
loop ()
(** Start an echo server on a random port, returns the port number *)
let start_echo_server ~sw net =
-
(* Listen on port 0 to get a random available port *)
let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 0) in
let listening_socket = Eio.Net.listen net ~sw ~backlog:128 ~reuse_addr:true addr in
-
-
(* Get the actual port assigned *)
let actual_addr = Eio.Net.listening_addr listening_socket in
let port = match actual_addr with
| `Tcp (_, port) -> port
| _ -> failwith "Expected TCP address"
in
-
Log.info (fun m -> m "Echo server started on port %d" port);
-
-
(* Start accepting connections in a daemon fiber.
-
The daemon runs until cancelled when the switch finishes. *)
Eio.Fiber.fork_daemon ~sw (fun () ->
try
while true do
Eio.Net.accept_fork ~sw listening_socket
-
~on_error:(fun ex ->
-
Log.warn (fun m -> m "Echo server error: %a" Fmt.exn ex))
+
~on_error:(fun _ -> ())
handle_echo_client
done;
`Stop_daemon
···
port
(** Client test: connect via pool, send message, verify echo *)
-
let run_client_test ~clock pool endpoint message test_stats =
+
let run_client_test ~clock ~collector pool endpoint endpoint_id message client_id latency_stats errors =
let msg_len = String.length message in
let start_time = Eio.Time.now clock in
+
(* Get or create connection ID for tracking *)
+
let conn_id = Trace.next_connection_id collector in
+
try
Conpool.with_connection pool endpoint (fun flow ->
+
(* Record acquire event *)
+
Trace.record collector ~clock ~event_type:Trace.Connection_acquired
+
~endpoint_id ~connection_id:conn_id ~client_id ();
+
(* Send message *)
Eio.Flow.copy_string message flow;
-
Eio.Flow.copy_string "\n" flow; (* delimiter *)
+
Eio.Flow.copy_string "\n" flow;
+
Trace.record collector ~clock ~event_type:Trace.Message_sent
+
~endpoint_id ~connection_id:conn_id ~client_id ();
(* Read echo response *)
let response = Eio.Buf_read.of_flow flow ~max_size:(msg_len + 1) in
let echoed = Eio.Buf_read.line response in
+
Trace.record collector ~clock ~event_type:Trace.Message_received
+
~endpoint_id ~connection_id:conn_id ~client_id ();
let end_time = Eio.Time.now clock in
-
let latency = end_time -. start_time in
+
let latency = (end_time -. start_time) *. 1000.0 in (* Convert to ms *)
if String.equal echoed message then begin
-
test_stats.total_messages <- test_stats.total_messages + 1;
-
test_stats.total_bytes <- test_stats.total_bytes + msg_len;
-
update_latency test_stats latency;
-
Log.debug (fun m -> m "Client: echoed %d bytes in %.3fms"
-
msg_len (latency *. 1000.0))
+
update_latency latency_stats latency;
+
Trace.record collector ~clock ~event_type:Trace.Message_verified
+
~endpoint_id ~connection_id:conn_id ~client_id ()
end else begin
-
test_stats.errors <- test_stats.errors + 1;
-
Log.err (fun m -> m "Client: echo mismatch! sent=%S got=%S" message echoed)
-
end
-
);
-
test_stats.total_connections <- test_stats.total_connections + 1
+
incr errors;
+
Trace.record collector ~clock ~event_type:(Trace.Connection_error "echo_mismatch")
+
~endpoint_id ~connection_id:conn_id ~client_id ()
+
end;
+
+
(* Record release event *)
+
Trace.record collector ~clock ~event_type:Trace.Connection_released
+
~endpoint_id ~connection_id:conn_id ~client_id ()
+
)
with ex ->
-
test_stats.errors <- test_stats.errors + 1;
-
Log.err (fun m -> m "Client error: %a" Fmt.exn ex)
+
incr errors;
+
Trace.record collector ~clock ~event_type:(Trace.Connection_error (Printexc.to_string ex))
+
~endpoint_id ~connection_id:conn_id ~client_id ()
(** Run a single client that sends multiple messages *)
-
let run_client ~clock pool endpoints config test_stats client_id =
-
Log.debug (fun m -> m "Starting client %d" client_id);
-
-
for msg_num = 1 to config.messages_per_client do
-
(* Pick a random endpoint *)
+
let run_client ~clock ~collector pool endpoints config latency_stats errors client_id =
+
for _ = 1 to config.messages_per_client do
let endpoint_idx = Random.int (Array.length endpoints) in
let endpoint = endpoints.(endpoint_idx) in
-
-
(* Generate unique message *)
-
let message = Printf.sprintf "client%d-msg%d-%s"
-
client_id msg_num (generate_message config.message_size) in
-
-
run_client_test ~clock pool endpoint message test_stats
-
done;
-
-
Log.debug (fun m -> m "Client %d completed" client_id)
+
let message = Printf.sprintf "c%d-%s" client_id (generate_message config.message_size) in
+
run_client_test ~clock ~collector pool endpoint endpoint_idx message client_id latency_stats errors
+
done
-
(** Main stress test runner *)
-
let run_stress_test ~env config =
+
(** Main stress test runner - returns a test trace *)
+
let run_stress_test ~env config : Trace.test_trace =
let net = Eio.Stdenv.net env in
let clock = Eio.Stdenv.clock env in
-
Log.info (fun m -> m "=== Stress Test Configuration ===");
-
Log.info (fun m -> m "Servers: %d" config.num_servers);
-
Log.info (fun m -> m "Clients per server: %d" config.num_clients);
-
Log.info (fun m -> m "Messages per client: %d" config.messages_per_client);
-
Log.info (fun m -> m "Max parallel clients: %d" config.max_parallel_clients);
-
Log.info (fun m -> m "Message size: %d bytes" config.message_size);
-
Log.info (fun m -> m "Pool size per endpoint: %d" config.pool_size);
-
-
(* Use a sub-switch for servers so we can cancel them when done *)
-
let test_passed = ref false in
-
let expected_messages = ref 0 in
-
-
Eio.Switch.run @@ fun sw ->
-
(* Start echo servers *)
-
Log.info (fun m -> m "Starting %d echo servers..." config.num_servers);
-
let ports = Array.init config.num_servers (fun _ ->
-
start_echo_server ~sw net
-
) in
+
let collector = Trace.create_collector () in
+
let latency_stats = create_latency_stats () in
+
let errors = ref 0 in
+
let ports = ref [||] in
-
(* Small delay to ensure servers are ready *)
-
Eio.Time.sleep clock 0.1;
+
let trace_config : Trace.test_config = {
+
num_servers = config.num_servers;
+
num_clients = config.num_clients;
+
messages_per_client = config.messages_per_client;
+
max_parallel_clients = config.max_parallel_clients;
+
message_size = config.message_size;
+
pool_size = config.pool_size;
+
} in
-
(* Create endpoints for all servers *)
-
let endpoints = Array.map (fun port ->
-
Conpool.Endpoint.make ~host:"127.0.0.1" ~port
-
) ports in
+
let start_unix_time = Unix.gettimeofday () in
-
Log.info (fun m -> m "Servers ready on ports: %s"
-
(String.concat ", " (Array.to_list (Array.map string_of_int ports))));
+
let result = ref None in
-
(* Create connection pool *)
-
let pool_config = Conpool.Config.make
-
~max_connections_per_endpoint:config.pool_size
-
~max_idle_time:30.0
-
~max_connection_lifetime:120.0
-
~connect_timeout:5.0
-
~connect_retry_count:3
-
()
-
in
+
begin
+
try
+
Eio.Switch.run @@ fun sw ->
+
(* Start echo servers *)
+
ports := Array.init config.num_servers (fun _ ->
+
start_echo_server ~sw net
+
);
-
let pool = Conpool.create ~sw ~net ~clock ~config:pool_config () in
-
Log.info (fun m -> m "Connection pool created");
+
Eio.Time.sleep clock 0.05;
-
(* Initialize test statistics *)
-
let test_stats = create_stats () in
+
let endpoints = Array.map (fun port ->
+
Conpool.Endpoint.make ~host:"127.0.0.1" ~port
+
) !ports in
-
(* Calculate total clients *)
-
let total_clients = config.num_servers * config.num_clients in
-
expected_messages := total_clients * config.messages_per_client;
-
Log.info (fun m -> m "Running %d total clients..." total_clients);
+
(* Create connection pool with hooks to track events *)
+
let pool_config = Conpool.Config.make
+
~max_connections_per_endpoint:config.pool_size
+
~max_idle_time:30.0
+
~max_connection_lifetime:120.0
+
~connect_timeout:5.0
+
~connect_retry_count:3
+
~on_connection_created:(fun ep ->
+
let port = Conpool.Endpoint.port ep in
+
let endpoint_id = Array.to_list !ports
+
|> List.mapi (fun i p -> (i, p))
+
|> List.find (fun (_, p) -> p = port)
+
|> fst in
+
let conn_id = Trace.next_connection_id collector in
+
Trace.record collector ~clock ~event_type:Trace.Connection_created
+
~endpoint_id ~connection_id:conn_id ()
+
)
+
~on_connection_reused:(fun ep ->
+
let port = Conpool.Endpoint.port ep in
+
let endpoint_id = Array.to_list !ports
+
|> List.mapi (fun i p -> (i, p))
+
|> List.find (fun (_, p) -> p = port)
+
|> fst in
+
let conn_id = Trace.next_connection_id collector in
+
Trace.record collector ~clock ~event_type:Trace.Connection_reused
+
~endpoint_id ~connection_id:conn_id ()
+
)
+
~on_connection_closed:(fun ep ->
+
let port = Conpool.Endpoint.port ep in
+
let endpoint_id = Array.to_list !ports
+
|> List.mapi (fun i p -> (i, p))
+
|> List.find (fun (_, p) -> p = port)
+
|> fst in
+
let conn_id = Trace.next_connection_id collector in
+
Trace.record collector ~clock ~event_type:Trace.Connection_closed
+
~endpoint_id ~connection_id:conn_id ()
+
)
+
()
+
in
-
let start_time = Eio.Time.now clock in
+
let pool = Conpool.create ~sw ~net ~clock ~config:pool_config () in
-
(* Run clients in parallel using Fiber.List *)
-
let client_ids = List.init total_clients (fun i -> i) in
-
Eio.Fiber.List.iter ~max_fibers:config.max_parallel_clients
-
(fun client_id ->
-
run_client ~clock pool endpoints config test_stats client_id)
-
client_ids;
+
(* Record start time *)
+
let start_time = Eio.Time.now clock in
+
Trace.set_start_time collector start_time;
-
let end_time = Eio.Time.now clock in
-
let total_time = end_time -. start_time in
+
(* Run clients in parallel *)
+
let total_clients = config.num_servers * config.num_clients in
+
let client_ids = List.init total_clients (fun i -> i) in
+
Eio.Fiber.List.iter ~max_fibers:config.max_parallel_clients
+
(fun client_id ->
+
run_client ~clock ~collector pool endpoints config latency_stats errors client_id)
+
client_ids;
-
(* Print results *)
-
Log.info (fun m -> m "");
-
Log.info (fun m -> m "=== Test Results ===");
-
Log.info (fun m -> m "Total time: %.3fs" total_time);
-
Log.info (fun m -> m "Total connections: %d" test_stats.total_connections);
-
Log.info (fun m -> m "Total messages: %d" test_stats.total_messages);
-
Log.info (fun m -> m "Total bytes transferred: %d" test_stats.total_bytes);
-
Log.info (fun m -> m "Errors: %d" test_stats.errors);
+
let end_time = Eio.Time.now clock in
+
let duration = end_time -. start_time in
-
if test_stats.total_messages > 0 then begin
-
let avg_latency = test_stats.total_latency /.
-
float_of_int test_stats.total_messages in
-
Log.info (fun m -> m "Latency (min/avg/max): %.3fms / %.3fms / %.3fms"
-
(test_stats.min_latency *. 1000.0)
-
(avg_latency *. 1000.0)
-
(test_stats.max_latency *. 1000.0));
-
Log.info (fun m -> m "Throughput: %.1f messages/sec"
-
(float_of_int test_stats.total_messages /. total_time));
-
Log.info (fun m -> m "Bandwidth: %.1f KB/sec"
-
(float_of_int test_stats.total_bytes /. total_time /. 1024.0))
-
end;
+
(* Build result *)
+
let events = Trace.get_events collector in
+
let endpoint_summaries = Trace.compute_endpoint_summaries events config.num_servers !ports in
-
(* Print pool statistics for each endpoint *)
-
Log.info (fun m -> m "");
-
Log.info (fun m -> m "=== Pool Statistics ===");
-
Array.iteri (fun i endpoint ->
-
let stats = Conpool.stats pool endpoint in
-
Log.info (fun m -> m "Endpoint %d (port %d):" i ports.(i));
-
Log.info (fun m -> m " Active: %d, Idle: %d"
-
(Conpool.Stats.active stats) (Conpool.Stats.idle stats));
-
Log.info (fun m -> m " Created: %d, Reused: %d, Closed: %d, Errors: %d"
-
(Conpool.Stats.total_created stats)
-
(Conpool.Stats.total_reused stats)
-
(Conpool.Stats.total_closed stats)
-
(Conpool.Stats.errors stats))
-
) endpoints;
+
result := Some {
+
Trace.test_name = config.name;
+
config = trace_config;
+
start_time = start_unix_time;
+
duration;
+
events;
+
endpoint_summaries;
+
total_messages = latency_stats.count;
+
total_errors = !errors;
+
throughput = float_of_int latency_stats.count /. duration;
+
avg_latency = if latency_stats.count > 0
+
then latency_stats.total /. float_of_int latency_stats.count
+
else 0.0;
+
min_latency = if latency_stats.count > 0 then latency_stats.min else 0.0;
+
max_latency = latency_stats.max;
+
};
-
(* Verify success *)
-
test_passed := test_stats.errors = 0 &&
-
test_stats.total_messages = !expected_messages;
+
Eio.Switch.fail sw Exit
+
with Exit -> ()
+
end;
-
if !test_passed then
-
Log.info (fun m -> m "TEST PASSED: All %d messages echoed successfully!"
-
!expected_messages)
-
else
-
Log.err (fun m -> m "TEST FAILED: Expected %d messages, got %d with %d errors"
-
!expected_messages test_stats.total_messages test_stats.errors);
+
match !result with
+
| Some r -> r
+
| None -> failwith "Test failed to produce result"
-
(* Cancel the switch to stop servers and exit cleanly *)
-
Eio.Switch.fail sw Exit
+
(** Run all preset tests and return traces *)
+
let run_all_presets ~env =
+
List.map (fun config ->
+
Printf.eprintf "Running test: %s\n%!" config.name;
+
run_stress_test ~env config
+
) presets
(** Parse command line arguments *)
-
let parse_config () =
+
type mode =
+
| Single of config
+
| AllPresets
+
| Extended
+
| ListPresets
+
+
let parse_args () =
+
let mode = ref (Single default_config) in
+
let name = ref default_config.name in
let num_servers = ref default_config.num_servers in
let num_clients = ref default_config.num_clients in
let messages_per_client = ref default_config.messages_per_client in
let max_parallel = ref default_config.max_parallel_clients in
let message_size = ref default_config.message_size in
let pool_size = ref default_config.pool_size in
-
let verbose = ref false in
+
let output_file = ref "stress_test_results.json" in
let specs = [
-
("-s", Arg.Set_int num_servers,
-
Printf.sprintf "Number of echo servers (default: %d)" default_config.num_servers);
-
("-c", Arg.Set_int num_clients,
-
Printf.sprintf "Clients per server (default: %d)" default_config.num_clients);
-
("-m", Arg.Set_int messages_per_client,
-
Printf.sprintf "Messages per client (default: %d)" default_config.messages_per_client);
-
("-p", Arg.Set_int max_parallel,
-
Printf.sprintf "Max parallel clients (default: %d)" default_config.max_parallel_clients);
-
("-b", Arg.Set_int message_size,
-
Printf.sprintf "Message size in bytes (default: %d)" default_config.message_size);
-
("-P", Arg.Set_int pool_size,
-
Printf.sprintf "Pool size per endpoint (default: %d)" default_config.pool_size);
-
("-v", Arg.Set verbose, "Enable verbose/debug logging");
+
("--all", Arg.Unit (fun () -> mode := AllPresets),
+
"Run all preset test configurations");
+
("--extended", Arg.Unit (fun () -> mode := Extended),
+
"Run extended stress test (30 servers, 1000 clients, 100 msgs each = 3M messages)");
+
("--list", Arg.Unit (fun () -> mode := ListPresets),
+
"List available presets");
+
("--preset", Arg.String (fun p ->
+
match List.find_opt (fun c -> c.name = p) presets with
+
| Some c -> mode := Single c
+
| None -> failwith (Printf.sprintf "Unknown preset: %s" p)),
+
"Use a named preset configuration");
+
("-n", Arg.Set_string name, "Test name");
+
("-s", Arg.Set_int num_servers, Printf.sprintf "Number of servers (default: %d)" default_config.num_servers);
+
("-c", Arg.Set_int num_clients, Printf.sprintf "Clients per server (default: %d)" default_config.num_clients);
+
("-m", Arg.Set_int messages_per_client, Printf.sprintf "Messages per client (default: %d)" default_config.messages_per_client);
+
("-p", Arg.Set_int max_parallel, Printf.sprintf "Max parallel clients (default: %d)" default_config.max_parallel_clients);
+
("-b", Arg.Set_int message_size, Printf.sprintf "Message size (default: %d)" default_config.message_size);
+
("-P", Arg.Set_int pool_size, Printf.sprintf "Pool size per endpoint (default: %d)" default_config.pool_size);
+
("-o", Arg.Set_string output_file, "Output JSON file (default: stress_test_results.json)");
] in
-
let usage = "Usage: stress_test [options]" in
+
let usage = "Usage: stress_test [options]\n\nOptions:" in
Arg.parse specs (fun _ -> ()) usage;
-
(* Configure logging *)
-
Logs.set_reporter (Logs_fmt.reporter ());
-
if !verbose then
-
Logs.set_level (Some Logs.Debug)
-
else
-
Logs.set_level (Some Logs.Info);
-
-
{
+
let config = {
+
name = !name;
num_servers = !num_servers;
num_clients = !num_clients;
messages_per_client = !messages_per_client;
max_parallel_clients = !max_parallel;
message_size = !message_size;
pool_size = !pool_size;
-
}
+
} in
+
+
(!mode, config, !output_file)
let () =
Random.self_init ();
-
let config = parse_config () in
-
Eio_main.run @@ fun env ->
-
(* Catch Exit which is used to signal clean shutdown *)
-
try run_stress_test ~env config
-
with Exit -> ()
+
let (mode, custom_config, output_file) = parse_args () in
+
+
match mode with
+
| ListPresets ->
+
Printf.printf "Available presets:\n";
+
List.iter (fun c ->
+
Printf.printf " %s: %d servers, %d clients, %d msgs/client, pool=%d\n"
+
c.name c.num_servers c.num_clients c.messages_per_client c.pool_size
+
) presets
+
+
| Single config ->
+
let config = if config.name = "default" then custom_config else config in
+
Eio_main.run @@ fun env ->
+
let trace = run_stress_test ~env config in
+
let json = Printf.sprintf "[%s]" (Trace.trace_to_json trace) in
+
let oc = open_out output_file in
+
output_string oc json;
+
close_out oc;
+
Printf.printf "Results written to %s\n" output_file;
+
Printf.printf "Test: %s - %d messages, %.2f msg/s, %.2fms avg latency, %d errors\n"
+
trace.test_name trace.total_messages trace.throughput trace.avg_latency trace.total_errors
+
+
| AllPresets ->
+
Eio_main.run @@ fun env ->
+
let traces = run_all_presets ~env in
+
let json = "[" ^ String.concat ",\n" (List.map Trace.trace_to_json traces) ^ "]" in
+
let oc = open_out output_file in
+
output_string oc json;
+
close_out oc;
+
Printf.printf "Results written to %s\n" output_file;
+
List.iter (fun t ->
+
Printf.printf " %s: %d messages, %.2f msg/s, %.2fms avg latency, %d errors\n"
+
t.Trace.test_name t.total_messages t.throughput t.avg_latency t.total_errors
+
) traces
+
+
| Extended ->
+
Printf.printf "Running extended stress test: %d servers, %d clients/server, %d msgs/client\n"
+
extended_preset.num_servers extended_preset.num_clients extended_preset.messages_per_client;
+
Printf.printf "Total messages: %d\n%!"
+
(extended_preset.num_servers * extended_preset.num_clients * extended_preset.messages_per_client);
+
Eio_main.run @@ fun env ->
+
let trace = run_stress_test ~env extended_preset in
+
let json = Printf.sprintf "[%s]" (Trace.trace_to_json trace) in
+
let oc = open_out output_file in
+
output_string oc json;
+
close_out oc;
+
Printf.printf "Results written to %s\n" output_file;
+
Printf.printf "Test: %s - %d messages, %.2f msg/s, %.2fms avg latency, %d errors\n"
+
trace.test_name trace.total_messages trace.throughput trace.avg_latency trace.total_errors