From 8e5d16e982eb5f2abc2ffeda2f193d0576d6ab1d Mon Sep 17 00:00:00 2001 From: vbot Date: Wed, 24 Jan 2024 14:01:43 +0100 Subject: [PATCH 1/4] RPC: limit the number of active RPC connections --- src/lib_rpc_http/RPC_server.ml | 6 ++++++ src/lib_rpc_http/RPC_server.mli | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/lib_rpc_http/RPC_server.ml b/src/lib_rpc_http/RPC_server.ml index f30baa04c58e..3f059156be60 100644 --- a/src/lib_rpc_http/RPC_server.ml +++ b/src/lib_rpc_http/RPC_server.ml @@ -304,3 +304,9 @@ module Acl = struct in Internal_for_test.resolve_domain_names resolve end + +let launch ?host server ?conn_closed ?callback ?(max_active_connections = 100) + mode = + (* TODO: backport max_active_connections in resto *) + Conduit_lwt_unix.set_max_active max_active_connections ; + launch ?host server ?conn_closed ?callback mode diff --git a/src/lib_rpc_http/RPC_server.mli b/src/lib_rpc_http/RPC_server.mli index c6f3a974fa24..e2f2786976ba 100644 --- a/src/lib_rpc_http/RPC_server.mli +++ b/src/lib_rpc_http/RPC_server.mli @@ -32,6 +32,29 @@ module RPC_logging : Resto_cohttp_server.Server.LOGGING include module type of Resto_cohttp_server.Server.Make (Tezos_rpc.Encoding) (RPC_logging) +(** [launch ?host server ?conn_closed ?callback + ?max_active_connections listening_protocol] starts the given resto + [server] initiating the listening loop using the + + @param [callback] overwrites (if given) the default handler of + each resto http query will be treated by. + + @param [conn_closed] is an optional function that is called when + a connection is closed. + + @param [max_active_connections] limits the number of active + connections. When this limit is reached, the server will not + process new requests until existing ones are + completed. Defaults to 100. *) +val launch : + ?host:string -> + server -> + ?conn_closed:(Cohttp_lwt_unix.Server.conn -> unit) -> + ?callback:callback -> + ?max_active_connections:int -> + Conduit_lwt_unix.server -> + unit Lwt.t + module Acl : sig include module type of Resto_acl.Acl -- GitLab From 84fccf343816492ffcf4977a70defde16fed4dcc Mon Sep 17 00:00:00 2001 From: vbot Date: Wed, 24 Jan 2024 14:26:20 +0100 Subject: [PATCH 2/4] Node: add option to configure the max allowed active RPC connections --- src/bin_node/node_run_command.ml | 9 ++++++++- src/lib_node_config/config_file.ml | 29 +++++++++++++++++++++++------ src/lib_node_config/config_file.mli | 4 ++++ src/lib_node_config/shared_arg.ml | 17 ++++++++++++++--- src/lib_node_config/shared_arg.mli | 1 + src/lib_rpc_process/main.ml | 9 ++++++++- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/bin_node/node_run_command.ml b/src/bin_node/node_run_command.ml index 24b75745275a..d14555767ee8 100644 --- a/src/bin_node/node_run_command.ml +++ b/src/bin_node/node_run_command.ml @@ -448,7 +448,14 @@ let launch_rpc_server (config : Config_file.t) dir rpc_server_kind addr = let mode = extract_mode rpc_server_kind in Lwt.catch (fun () -> - let*! () = RPC_server.launch ~host server ~callback mode in + let*! () = + RPC_server.launch + ~host + server + ~callback + ~max_active_connections:config.rpc.max_active_rpc_connections + mode + in return server) (function (* FIXME: https://gitlab.com/tezos/tezos/-/issues/1312 diff --git a/src/lib_node_config/config_file.ml b/src/lib_node_config/config_file.ml index f9c9c3c77aa8..70c353d4887a 100644 --- a/src/lib_node_config/config_file.ml +++ b/src/lib_node_config/config_file.ml @@ -34,6 +34,8 @@ let default_data_dir = home // ".tezos-node" let default_rpc_port = 8732 +let default_max_active_rpc_connections = 100 + let default_metrics_port = 9932 let default_p2p_port = 9732 @@ -361,6 +363,7 @@ and rpc = { tls : tls option; acl : RPC_server.Acl.policy; media_type : Media_type.Command_line.t; + max_active_rpc_connections : int; } and tls = {cert : string; key : string} @@ -389,6 +392,7 @@ let default_rpc = tls = None; acl = RPC_server.Acl.empty_policy; media_type = Media_type.Command_line.Any; + max_active_rpc_connections = default_max_active_rpc_connections; } let default_disable_config_validation = false @@ -559,6 +563,7 @@ let rpc : rpc Data_encoding.t = tls; acl; media_type; + max_active_rpc_connections; } -> let cert, key = match tls with @@ -573,7 +578,8 @@ let rpc : rpc Data_encoding.t = cert, key, acl, - media_type )) + media_type, + max_active_rpc_connections )) (fun ( listen_addrs, local_listen_addrs, legacy_listen_addr, @@ -582,7 +588,8 @@ let rpc : rpc Data_encoding.t = cert, key, acl, - media_type ) -> + media_type, + max_active_rpc_connections ) -> let tls = match (cert, key) with | None, _ | _, None -> None @@ -609,8 +616,9 @@ let rpc : rpc Data_encoding.t = tls; acl; media_type; + max_active_rpc_connections; }) - (obj9 + (obj10 (opt "listen-addrs" ~description: @@ -652,7 +660,13 @@ let rpc : rpc Data_encoding.t = "media-type" ~description:"The media types supported by the server." Media_type.Command_line.encoding - default_rpc.media_type)) + default_rpc.media_type) + (dft + "max_active_rpc_connections" + ~description: + "The maximum number of active connections per RPC endpoint." + int31 + default_rpc.max_active_rpc_connections)) let rpc_encoding = rpc @@ -840,8 +854,10 @@ let update ?(disable_config_validation = false) ?data_dir ?min_connections ?binary_chunks_size ?peer_table_size ?expected_pow ?bootstrap_peers ?listen_addr ?advertised_net_port ?discovery_addr ?(rpc_listen_addrs = []) ?(local_rpc_listen_addrs = []) ?(allow_all_rpc = []) - ?(media_type = Media_type.Command_line.Any) ?(metrics_addr = []) - ?operation_metadata_size_limit ?(private_mode = default_p2p.private_mode) + ?(media_type = Media_type.Command_line.Any) + ?(max_active_rpc_connections = default_rpc.max_active_rpc_connections) + ?(metrics_addr = []) ?operation_metadata_size_limit + ?(private_mode = default_p2p.private_mode) ?(disable_p2p_maintenance = Option.is_none default_p2p.limits.maintenance_idle_time) ?(disable_p2p_swap = Option.is_none default_p2p.limits.swap_linger) @@ -928,6 +944,7 @@ let update ?(disable_config_validation = false) ?data_dir ?min_connections tls = Option.either rpc_tls cfg.rpc.tls; acl; media_type; + max_active_rpc_connections; } and metrics_addr = unopt_list ~default:cfg.metrics_addr metrics_addr and log : Logs_simple_config.cfg = diff --git a/src/lib_node_config/config_file.mli b/src/lib_node_config/config_file.mli index 9a63c29b3456..563faa1a9300 100644 --- a/src/lib_node_config/config_file.mli +++ b/src/lib_node_config/config_file.mli @@ -81,6 +81,7 @@ and rpc = { tls : tls option; acl : RPC_server.Acl.policy; media_type : Media_type.Command_line.t; + max_active_rpc_connections : int; } and tls = {cert : string; key : string} @@ -93,6 +94,8 @@ val default_p2p_port : int val default_rpc_port : int +val default_max_active_rpc_connections : int + val default_p2p : p2p val default_config : t @@ -120,6 +123,7 @@ val update : ?local_rpc_listen_addrs:string list -> ?allow_all_rpc:P2p_point.Id.addr_port_id list -> ?media_type:Media_type.Command_line.t -> + ?max_active_rpc_connections:int -> ?metrics_addr:string list -> ?operation_metadata_size_limit:Shell_limits.operation_metadata_size_limit -> ?private_mode:bool -> diff --git a/src/lib_node_config/shared_arg.ml b/src/lib_node_config/shared_arg.ml index 8a095fa82f72..6d5a92ad2c84 100644 --- a/src/lib_node_config/shared_arg.ml +++ b/src/lib_node_config/shared_arg.ml @@ -66,6 +66,7 @@ type t = { latency : int option; allow_all_rpc : P2p_point.Id.addr_port_id list; media_type : Media_type.Command_line.t; + max_active_rpc_connections : int option; metrics_addr : string list; operation_metadata_size_limit : Shell_limits.operation_metadata_size_limit option; @@ -188,8 +189,8 @@ let wrap data_dir config_file network connections max_download_speed disable_mempool enable_testchain expected_pow rpc_listen_addrs local_rpc_listen_addrs rpc_tls cors_origins cors_headers log_output log_coloring history_mode synchronisation_threshold latency - disable_config_validation allow_all_rpc media_type metrics_addr - operation_metadata_size_limit = + disable_config_validation allow_all_rpc media_type + max_active_rpc_connections metrics_addr operation_metadata_size_limit = let actual_data_dir = Option.value ~default:Config_file.default_data_dir data_dir in @@ -235,6 +236,7 @@ let wrap data_dir config_file network connections max_download_speed latency; allow_all_rpc; media_type; + max_active_rpc_connections; metrics_addr; operation_metadata_size_limit; } @@ -722,6 +724,13 @@ module Term = struct Media_type.Command_line.Any & info ~docs ~doc ~docv:"MEDIATYPE" ["media-type"]) + let max_active_rpc_connections = + let doc = "Sets the maximum number of active connections per RPC server." in + Arg.( + value + & opt (some int) (Some Config_file.default_max_active_rpc_connections) + & info ~docs ~doc ~docv:"NUM" ["max-active-rpc-connections"]) + (* Args. *) let args = @@ -735,7 +744,7 @@ module Term = struct $ local_rpc_listen_addrs $ rpc_tls $ cors_origins $ cors_headers $ log_output $ log_coloring $ history_mode $ synchronisation_threshold $ latency $ disable_config_validation $ allow_all_rpc $ media_type - $ metrics_addr $ operation_metadata_size_limit + $ max_active_rpc_connections $ metrics_addr $ operation_metadata_size_limit end let read_config_file args = @@ -880,6 +889,7 @@ let patch_config ?(may_override_network = false) ?(emit = Event.emit) latency; allow_all_rpc; media_type; + max_active_rpc_connections; metrics_addr; operation_metadata_size_limit; } = @@ -1023,6 +1033,7 @@ let patch_config ?(may_override_network = false) ?(emit = Event.emit) ~local_rpc_listen_addrs ~allow_all_rpc ~media_type + ?max_active_rpc_connections ~metrics_addr ?operation_metadata_size_limit ~private_mode diff --git a/src/lib_node_config/shared_arg.mli b/src/lib_node_config/shared_arg.mli index 3328959a4b2d..efa96598d35c 100644 --- a/src/lib_node_config/shared_arg.mli +++ b/src/lib_node_config/shared_arg.mli @@ -81,6 +81,7 @@ type t = { (** a list of RPC listening addresses for which a full access should be granted *) media_type : Media_type.Command_line.t; + max_active_rpc_connections : int option; metrics_addr : string list; operation_metadata_size_limit : Shell_limits.operation_metadata_size_limit option; diff --git a/src/lib_rpc_process/main.ml b/src/lib_rpc_process/main.ml index c7cc282648d8..7e6be754e7e9 100644 --- a/src/lib_rpc_process/main.ml +++ b/src/lib_rpc_process/main.ml @@ -114,7 +114,14 @@ let launch_rpc_server (params : Parameters.t) (addr, port) = in Lwt.catch (fun () -> - let*! () = RPC_server.launch ~host server ~callback mode in + let*! () = + RPC_server.launch + ~host + server + ~callback + ~max_active_connections:params.config.rpc.max_active_rpc_connections + mode + in return server) (function (* FIXME: https://gitlab.com/tezos/tezos/-/issues/1312 -- GitLab From 58fbd09c4f9f3d4b0b1c6f00d60c0defb2d50170 Mon Sep 17 00:00:00 2001 From: vbot Date: Fri, 26 Jan 2024 17:43:13 +0100 Subject: [PATCH 3/4] Tezt: add --max-active-rpc-connections and set it to 500 by default --- tezt/lib_tezos/node.ml | 16 +++++++++++++--- tezt/lib_tezos/node.mli | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tezt/lib_tezos/node.ml b/tezt/lib_tezos/node.ml index 893dc5ccc365..290065f687ac 100644 --- a/tezt/lib_tezos/node.ml +++ b/tezt/lib_tezos/node.ml @@ -58,6 +58,7 @@ type argument = | Version | RPC_additional_addr of string | RPC_additional_addr_local of string + | Max_active_rpc_connections of int let make_argument = function | Network x -> ["--network"; x] @@ -89,6 +90,8 @@ let make_argument = function | Version -> ["--version"] | RPC_additional_addr addr -> ["--rpc-addr"; addr] | RPC_additional_addr_local addr -> ["--local-rpc-addr"; addr] + | Max_active_rpc_connections n -> + ["--max-active-rpc-connections"; string_of_int n] let make_arguments arguments = List.flatten (List.map make_argument arguments) @@ -110,7 +113,8 @@ let is_redundant = function | No_bootstrap_peers, No_bootstrap_peers | Media_type _, Media_type _ | Metadata_size_limit _, Metadata_size_limit _ - | Version, Version -> + | Version, Version + | Max_active_rpc_connections _, Max_active_rpc_connections _ -> true | Metrics_addr addr1, Metrics_addr addr2 -> addr1 = addr2 | Peer peer1, Peer peer2 -> peer1 = peer2 @@ -134,7 +138,8 @@ let is_redundant = function | Disable_mempool, _ | RPC_additional_addr _, _ | RPC_additional_addr_local _, _ - | Version, _ -> + | Version, _ + | Max_active_rpc_connections _, _ -> false (* Some arguments should not be written in the config file by [Node.init] @@ -163,6 +168,7 @@ module Parameters = struct rpc_port : int; rpc_tls : tls_config option; allow_all_rpc : bool; + max_active_rpc_connections : int; default_expected_pow : int; mutable default_arguments : argument list; mutable arguments : argument list; @@ -707,7 +713,8 @@ let wait_for_disconnections node disconnections = let create ?runner ?(path = Uses.path Constant.octez_node) ?name ?color ?data_dir ?event_pipe ?net_addr ?net_port ?advertised_net_port ?metrics_addr ?metrics_port ?(rpc_local = false) ?(rpc_host = Constant.default_host) - ?rpc_port ?rpc_tls ?(allow_all_rpc = true) arguments = + ?rpc_port ?rpc_tls ?(allow_all_rpc = true) + ?(max_active_rpc_connections = 500) arguments = let name = match name with None -> fresh_name () | Some name -> name in let data_dir = match data_dir with None -> Temp.dir ?runner name | Some dir -> dir @@ -744,6 +751,7 @@ let create ?runner ?(path = Uses.path Constant.octez_node) ?name ?color rpc_tls; metrics_addr; metrics_port; + max_active_rpc_connections; allow_all_rpc; default_arguments = arguments; arguments; @@ -853,6 +861,8 @@ let runlike_command_arguments node command arguments = :: (if node.persistent_state.rpc_local then "--local-rpc-addr" else "--rpc-addr") :: (rpc_addr ^ string_of_int node.persistent_state.rpc_port) + :: "--max-active-rpc-connections" + :: string_of_int node.persistent_state.max_active_rpc_connections :: command_args let do_runlike_command ?(on_terminate = fun _ -> ()) ?event_level diff --git a/tezt/lib_tezos/node.mli b/tezt/lib_tezos/node.mli index 3cd54ea54114..4c85dd35f898 100644 --- a/tezt/lib_tezos/node.mli +++ b/tezt/lib_tezos/node.mli @@ -98,6 +98,7 @@ type argument = | Version (** [--version] *) | RPC_additional_addr of string (** [--rpc-addr] *) | RPC_additional_addr_local of string (** [--local-rpc-addr] *) + | Max_active_rpc_connections of int (** [--max-active-rpc-connections] *) (** A TLS configuration for the node: paths to a [.crt] and a [.key] file. @@ -138,6 +139,8 @@ type t Default value for [allow_all_rpc] is [true]. + Default value for [max_active_rpc_connections] is [500]. + The argument list is a list of configuration options that the node should run with. It is passed to the first run of [octez-node config init]. It is also passed to all runs of [octez-node run] that occur before @@ -163,6 +166,7 @@ val create : ?rpc_port:int -> ?rpc_tls:tls_config -> ?allow_all_rpc:bool -> + ?max_active_rpc_connections:int -> argument list -> t -- GitLab From 3522e34041c1ac23e889ed8b5d83a0578c39e615 Mon Sep 17 00:00:00 2001 From: vbot Date: Wed, 24 Jan 2024 14:32:07 +0100 Subject: [PATCH 4/4] Changelog: add --max-active-rpc-connections entry --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0421da0e6c27..47f3330adaa6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -35,6 +35,10 @@ Node - Introduced a new ``--local-rpc-addr`` that starts the RPC server locally, not using the dedicated RPC-process. +- Added ``--max-active-rpc-connections `` that limits the number + of active RPC connections per server to the provided argument. The + default limit is set to 100. + Client ------ -- GitLab