open Swim.Types module Cluster = Swim.Cluster external env_cast : 'a -> 'b = "%identity" type node_result = { port : int; messages_sent : int; messages_received : int; members_seen : int; memory_bytes : int; elapsed_sec : float; } let result_to_json r = Printf.sprintf {|{"port":%d,"messages_sent":%d,"messages_received":%d,"members_seen":%d,"memory_bytes":%d,"elapsed_sec":%.3f}|} r.port r.messages_sent r.messages_received r.members_seen r.memory_bytes r.elapsed_sec let make_config ~port ~name = { default_config with bind_addr = "\127\000\000\001"; bind_port = port; node_name = Some name; protocol_interval = 0.2; probe_timeout = 0.1; suspicion_mult = 2; secret_key = String.make 16 'k'; cluster_name = ""; encryption_enabled = false; } let run_node ~env ~port ~peers ~duration_sec = Gc.full_major (); let mem_before = (Gc.stat ()).Gc.live_words * (Sys.word_size / 8) in let start_time = Unix.gettimeofday () in Eio.Switch.run @@ fun sw -> let config = make_config ~port ~name:(Printf.sprintf "node-%d" port) in let env_wrap = { stdenv = env; sw } in match Cluster.create ~sw ~env:env_wrap ~config with | Error `Invalid_key -> Unix._exit 1 | Ok cluster -> Cluster.start cluster; List.iter (fun peer_port -> if peer_port <> port then let peer_id = node_id_of_string (Printf.sprintf "node-%d" peer_port) in let peer_addr = `Udp (Eio.Net.Ipaddr.of_raw "\127\000\000\001", peer_port) in let peer = make_node_info ~id:peer_id ~addr:peer_addr ~meta:"" in Cluster.add_member cluster peer) peers; Eio.Time.sleep env#clock duration_sec; let s = Cluster.stats cluster in let members = Cluster.members cluster in Gc.full_major (); let mem_after = (Gc.stat ()).Gc.live_words * (Sys.word_size / 8) in let elapsed = Unix.gettimeofday () -. start_time in let result = { port; messages_sent = s.msgs_sent; messages_received = s.msgs_received; members_seen = List.length members; memory_bytes = max 0 (mem_after - mem_before); elapsed_sec = elapsed; } in print_endline (result_to_json result); flush stdout; Unix._exit 0 let parse_peers s = String.split_on_char ',' s |> List.filter (fun s -> String.length s > 0) |> List.map int_of_string let () = let port = ref 0 in let peers_str = ref "" in let duration_sec = ref 10.0 in let specs = [ ("-port", Arg.Set_int port, "Port to bind to (required)"); ("-peers", Arg.Set_string peers_str, "Comma-separated peer ports"); ("-duration", Arg.Set_float duration_sec, "Duration in seconds"); ] in Arg.parse specs (fun _ -> ()) "SWIM Single Node Benchmark"; if !port = 0 then ( Printf.eprintf "Error: -port is required\n"; exit 1); let peers = parse_peers !peers_str in Eio_main.run @@ fun env -> let env = env_cast env in run_node ~env ~port:!port ~peers ~duration_sec:!duration_sec