diff --git a/CHANGES.rst b/CHANGES.rst index 9ae4ea61c3fc74d7695f9c075b5d14bd38c84bfe..b6b8c3a316b2655c5c12be9118ce56204c317271 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -54,6 +54,10 @@ Node RPC. Instead, use the ``/monitor/applied_blocks`` RPC that has the same behaviour. +- 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 ------ diff --git a/src/bin_node/node_run_command.ml b/src/bin_node/node_run_command.ml index 69814fae9cca378173aa701a1c2fd0ec13621d1e..ec9b9e1fd614299c9eca310173e617280f116609 100644 --- a/src/bin_node/node_run_command.ml +++ b/src/bin_node/node_run_command.ml @@ -431,7 +431,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 d6146d275975d5473d48d09031ddb1f58bf8015b..f75a35494f1e31e31bd062aae12daa62a9bccc52 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 @@ -360,6 +362,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} @@ -387,6 +390,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 @@ -549,7 +553,15 @@ let p2p = let rpc : rpc Data_encoding.t = let open Data_encoding in conv - (fun {cors_origins; cors_headers; listen_addrs; tls; acl; media_type} -> + (fun { + cors_origins; + cors_headers; + listen_addrs; + tls; + acl; + media_type; + max_active_rpc_connections; + } -> let cert, key = match tls with | None -> (None, None) @@ -562,7 +574,8 @@ let rpc : rpc Data_encoding.t = cert, key, acl, - media_type )) + media_type, + max_active_rpc_connections )) (fun ( listen_addrs, legacy_listen_addr, cors_origins, @@ -570,7 +583,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 @@ -586,8 +600,16 @@ let rpc : rpc Data_encoding.t = "Config file: Use only \"listen-addrs\" and not (legacy) \ \"listen-addr\"." in - {listen_addrs; cors_origins; cors_headers; tls; acl; media_type}) - (obj8 + { + listen_addrs; + cors_origins; + cors_headers; + tls; + acl; + media_type; + max_active_rpc_connections; + }) + (obj9 (opt "listen-addrs" ~description: @@ -623,7 +645,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 @@ -811,6 +839,7 @@ 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 = []) ?(allow_all_rpc = []) ?(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 = @@ -897,6 +926,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 6100bb26263cf4d0555b7d1764ba560c693edbf5..3f540380620120abe6b0a1c36ab32f05e7962fd9 100644 --- a/src/lib_node_config/config_file.mli +++ b/src/lib_node_config/config_file.mli @@ -80,6 +80,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} @@ -92,6 +93,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 @@ -118,6 +121,7 @@ val update : ?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 30234945e6160384066ecd26a32beb1ec183442f..b413bbdb98e437dee96e8f946fece4408af1256e 100644 --- a/src/lib_node_config/shared_arg.ml +++ b/src/lib_node_config/shared_arg.ml @@ -65,6 +65,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; @@ -187,7 +188,8 @@ let wrap data_dir config_file network connections max_download_speed disable_mempool enable_testchain expected_pow 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 = + 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 @@ -232,6 +234,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; } @@ -709,6 +712,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 = @@ -721,7 +731,8 @@ module Term = struct $ enable_testchain $ expected_pow $ 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 + $ allow_all_rpc $ media_type $ max_active_rpc_connections $ metrics_addr + $ operation_metadata_size_limit end let read_config_file args = @@ -865,6 +876,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; } = @@ -1007,6 +1019,7 @@ let patch_config ?(may_override_network = false) ?(emit = Event.emit) ~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 5d574693159b335316055180c8b8702db53ee36e..4dcd507accb8bcb3df2e2efa22a5158758e4f7b4 100644 --- a/src/lib_node_config/shared_arg.mli +++ b/src/lib_node_config/shared_arg.mli @@ -79,6 +79,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_http/RPC_server.ml b/src/lib_rpc_http/RPC_server.ml index a49c611709aaa9179434748e1ddb8d29c7ac8e10..b7d75aa9a1e9c96d5024e652406e0427cede3a3f 100644 --- a/src/lib_rpc_http/RPC_server.ml +++ b/src/lib_rpc_http/RPC_server.ml @@ -306,3 +306,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 c6f3a974fa246e38fa766e5059503b3ed4acab98..e2f2786976ba2377f706b3af5dc25aefef298d73 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 diff --git a/src/lib_rpc_process/main.ml b/src/lib_rpc_process/main.ml index c7cc282648d8719b3a57aee77f7ee00941046ef7..7e6be754e7e909210f35336fd96d530a3e6a9c59 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 diff --git a/tezt/lib_tezos/node.ml b/tezt/lib_tezos/node.ml index c6236c8c4bad935cc94d4107d72cb99673c1fa2b..13a8226b3ee71062a162969bf4198befea4a75f3 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 type 'a known = Unknown | Known of 'a @@ -152,6 +157,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; @@ -696,7 +702,8 @@ let wait_for_disconnections node disconnections = let create ?runner ?(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 = "localhost") ?rpc_port - ?rpc_tls ?(allow_all_rpc = true) arguments = + ?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 @@ -733,6 +740,7 @@ let create ?runner ?(path = Constant.octez_node) ?name ?color ?data_dir rpc_tls; metrics_addr; metrics_port; + max_active_rpc_connections; allow_all_rpc; default_arguments = arguments; arguments; @@ -837,6 +845,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 fcdfa1160d83838ce376826105ce78fd97629f02..9622bf5c14be146051db45f7824fba70a5a8c47c 100644 --- a/tezt/lib_tezos/node.mli +++ b/tezt/lib_tezos/node.mli @@ -86,6 +86,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. @@ -126,6 +127,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 @@ -151,6 +154,7 @@ val create : ?rpc_port:int -> ?rpc_tls:tls_config -> ?allow_all_rpc:bool -> + ?max_active_rpc_connections:int -> argument list -> t