diff --git a/Cargo.lock b/Cargo.lock index e7c46cf7..90342d28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2361,6 +2361,7 @@ dependencies = [ "cyphernet", "fastrand", "git2", + "libc", "localtime", "log", "multibase", diff --git a/radicle-cli/examples/rad-config.md b/radicle-cli/examples/rad-config.md index 09ffa2ec..5fc6894d 100644 --- a/radicle-cli/examples/rad-config.md +++ b/radicle-cli/examples/rad-config.md @@ -25,6 +25,7 @@ $ rad config "routingMaxAge": 604800, "gossipMaxAge": 1209600, "fetchConcurrency": 1, + "maxOpenFiles": 4096, "rate": { "inbound": { "fillRate": 0.2, diff --git a/radicle-httpd/src/api/v1/profile.rs b/radicle-httpd/src/api/v1/profile.rs index 6f4d6cd5..ce703c0d 100644 --- a/radicle-httpd/src/api/v1/profile.rs +++ b/radicle-httpd/src/api/v1/profile.rs @@ -94,6 +94,7 @@ mod routes { "routingMaxAge": 604800, "gossipMaxAge": 1209600, "fetchConcurrency": 1, + "maxOpenFiles": 4096, "rate": { "inbound": { "fillRate": 0.2, diff --git a/radicle-node/src/main.rs b/radicle-node/src/main.rs index 2e094995..010d98b9 100644 --- a/radicle-node/src/main.rs +++ b/radicle-node/src/main.rs @@ -108,6 +108,10 @@ fn execute() -> anyhow::Result<()> { config.listen.clone() }; + if let Err(e) = radicle::io::set_file_limit(config.limits.max_open_files as u64) { + log::warn!(target: "node", "Unable to set process open file limit: {e}"); + } + let (notify, signals) = chan::bounded(1); signals::install(notify)?; diff --git a/radicle/Cargo.toml b/radicle/Cargo.toml index 16e01724..40c5f360 100644 --- a/radicle/Cargo.toml +++ b/radicle/Cargo.toml @@ -21,6 +21,7 @@ cyphernet = { version = "0.4.1", features = ["tor", "dns", "p2p-ed25519"] } fastrand = { version = "2.0.0" } multibase = { version = "0.9.1" } localtime = { version = "1.2.0", features = ["serde"] } +libc = { version = "0.2" } log = { version = "0.4.17", features = ["std"] } nonempty = { version = "0.8.1", features = ["serialize"] } once_cell = { version = "1.13" } diff --git a/radicle/src/io.rs b/radicle/src/io.rs new file mode 100644 index 00000000..f1d8aa17 --- /dev/null +++ b/radicle/src/io.rs @@ -0,0 +1,25 @@ +use std::io; + +use libc::{getrlimit, rlimit, setrlimit, RLIMIT_NOFILE}; + +/// Sets the open file limit to the given value, or the maximum allowed value. +pub fn set_file_limit(n: u64) -> io::Result { + let mut rlim = rlimit { + rlim_cur: 0, // Initial soft limit value + rlim_max: 0, // Initial hard limit value + }; + // Get the current limits. + unsafe { + if getrlimit(RLIMIT_NOFILE, &mut rlim) != 0 { + return Err(io::Error::last_os_error()); + } + } + // Set the soft limit to the given value, up to the hard limit. + rlim.rlim_cur = n.min(rlim.rlim_max); + unsafe { + if setrlimit(RLIMIT_NOFILE, &rlim as *const rlimit) != 0 { + return Err(io::Error::last_os_error()); + } + } + Ok(rlim.rlim_cur) +} diff --git a/radicle/src/lib.rs b/radicle/src/lib.rs index b6e48468..83886a4e 100644 --- a/radicle/src/lib.rs +++ b/radicle/src/lib.rs @@ -16,6 +16,7 @@ pub mod collections; pub mod explorer; pub mod git; pub mod identity; +pub mod io; #[cfg(feature = "logger")] pub mod logger; pub mod node; diff --git a/radicle/src/node/config.rs b/radicle/src/node/config.rs index 9281eab7..db372266 100644 --- a/radicle/src/node/config.rs +++ b/radicle/src/node/config.rs @@ -80,6 +80,8 @@ pub struct Limits { pub gossip_max_age: LocalDuration, /// Maximum number of concurrent fetches per per connection. pub fetch_concurrency: usize, + /// Maximum number of open files. + pub max_open_files: usize, /// Rate limitter settings. #[serde(default)] pub rate: RateLimits, @@ -92,6 +94,7 @@ impl Default for Limits { routing_max_age: LocalDuration::from_mins(7 * 24 * 60), // One week gossip_max_age: LocalDuration::from_mins(2 * 7 * 24 * 60), // Two weeks fetch_concurrency: 1, + max_open_files: 4096, rate: RateLimits::default(), } }