From c4ff0d8eae98a8334ccb2e668b646a9264a544d5 Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Tue, 12 Aug 2025 15:49:12 +0200 Subject: [PATCH] radicle/config/node: Granular Default Values Provide default values not only per struct, but for each individual struct member. This allows more granular configuration and fallback to defaults. --- crates/radicle/src/node/config.rs | 158 +++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 25 deletions(-) diff --git a/crates/radicle/src/node/config.rs b/crates/radicle/src/node/config.rs index d65df8af..1260cb0b 100644 --- a/crates/radicle/src/node/config.rs +++ b/crates/radicle/src/node/config.rs @@ -15,9 +15,6 @@ use crate::node::{Address, Alias, NodeId}; /// Peer-to-peer protocol version. pub type ProtocolVersion = u8; -/// Default number of workers to spawn. -pub const DEFAULT_WORKERS: usize = 8; - /// Configured public seeds. pub mod seeds { use std::{ @@ -128,31 +125,47 @@ impl Network { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Limits { /// Number of routing table entries before we start pruning. + #[serde(default = "defaults::limit_routing_max_size")] pub routing_max_size: usize, + /// How long to keep a routing table entry before being pruned. - #[serde(with = "crate::serde_ext::localtime::duration")] + #[serde( + default = "defaults::limit_routing_max_age", + with = "crate::serde_ext::localtime::duration" + )] #[cfg_attr( feature = "schemars", schemars(with = "crate::schemars_ext::localtime::LocalDuration") )] pub routing_max_age: LocalDuration, + /// How long to keep a gossip message entry before pruning it. - #[serde(with = "crate::serde_ext::localtime::duration")] + #[serde( + default = "defaults::limit_gossip_max_age", + with = "crate::serde_ext::localtime::duration" + )] #[cfg_attr( feature = "schemars", schemars(with = "crate::schemars_ext::localtime::LocalDuration") )] pub gossip_max_age: LocalDuration, + /// Maximum number of concurrent fetches per peer connection. + #[serde(default = "defaults::limit_fetch_concurrency")] pub fetch_concurrency: usize, + /// Maximum number of open files. + #[serde(default = "defaults::limit_max_open_files")] pub max_open_files: usize, + /// Rate limitter settings. #[serde(default)] pub rate: RateLimits, + /// Connection limits. #[serde(default)] pub connection: ConnectionLimits, + /// Channel limits. #[serde(default)] pub fetch_pack_receive: FetchPackSizeLimit, @@ -161,11 +174,11 @@ pub struct Limits { impl Default for Limits { fn default() -> Self { Self { - routing_max_size: 1000, - 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, + routing_max_size: defaults::limit_routing_max_size(), + routing_max_age: defaults::limit_routing_max_age(), + gossip_max_age: defaults::limit_gossip_max_age(), + fetch_concurrency: defaults::limit_fetch_concurrency(), + max_open_files: defaults::limit_max_open_files(), rate: RateLimits::default(), connection: ConnectionLimits::default(), fetch_pack_receive: FetchPackSizeLimit::default(), @@ -269,16 +282,19 @@ impl Default for FetchPackSizeLimit { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ConnectionLimits { /// Max inbound connections. + #[serde(default = "defaults::limit_connections_inbound")] pub inbound: usize, + /// Max outbound connections. Note that this can be higher than the *target* number. + #[serde(default = "defaults::limit_connections_outbound")] pub outbound: usize, } impl Default for ConnectionLimits { fn default() -> Self { Self { - inbound: 128, - outbound: 16, + inbound: defaults::limit_connections_inbound(), + outbound: defaults::limit_connections_outbound(), } } } @@ -297,21 +313,18 @@ pub struct RateLimit { #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct RateLimits { + #[serde(default = "defaults::limit_rate_inbound")] pub inbound: RateLimit, + + #[serde(default = "defaults::limit_rate_outbound")] pub outbound: RateLimit, } impl Default for RateLimits { fn default() -> Self { Self { - inbound: RateLimit { - fill_rate: 5.0, - capacity: 1024, - }, - outbound: RateLimit { - fill_rate: 10.0, - capacity: 2048, - }, + inbound: defaults::limit_rate_inbound(), + outbound: defaults::limit_rate_outbound(), } } } @@ -536,7 +549,7 @@ impl Config { onion: None, relay: Relay::default(), limits: Limits::default(), - workers: DEFAULT_WORKERS, + workers: defaults::workers(), log: defaults::log(), seeding_policy: DefaultSeedingPolicy::default(), extra: json::Map::default(), @@ -576,13 +589,108 @@ impl Config { /// Defaults as functions, for serde. mod defaults { - /// Worker count. - pub fn workers() -> usize { - super::DEFAULT_WORKERS + /// Default number of workers to spawn. + #[inline] + pub const fn workers() -> usize { + 8 } /// Log level. - pub fn log() -> log::Level { + #[inline] + pub const fn log() -> log::Level { log::Level::Info } + + #[inline] + pub const fn limit_connections_inbound() -> usize { + 128 + } + + #[inline] + pub const fn limit_connections_outbound() -> usize { + 16 + } + + #[inline] + pub const fn limit_routing_max_size() -> usize { + 1000 + } + + #[inline] + pub const fn limit_routing_max_age() -> localtime::LocalDuration { + localtime::LocalDuration::from_mins(7 * 24 * 60) // One week + } + + #[inline] + pub const fn limit_gossip_max_age() -> localtime::LocalDuration { + localtime::LocalDuration::from_mins(2 * 7 * 24 * 60) // Two weeks + } + + #[inline] + pub const fn limit_fetch_concurrency() -> usize { + 1 + } + + #[inline] + pub const fn limit_max_open_files() -> usize { + 4096 + } + + #[inline] + pub const fn limit_rate_inbound() -> super::RateLimit { + super::RateLimit { + fill_rate: 5.0, + capacity: 1024, + } + } + + #[inline] + pub const fn limit_rate_outbound() -> super::RateLimit { + super::RateLimit { + fill_rate: 10.0, + capacity: 2048, + } + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod test { + #[test] + fn partial() { + use super::Config; + use serde_json::json; + + let config: Config = serde_json::from_value(json!({ + "alias": "example", + "limits": { + "connection": { + "inbound": 1337, + }, + }, + } + )) + .unwrap(); + assert_eq!(config.limits.connection.inbound, 1337); + assert_eq!( + config.limits.connection.outbound, + super::defaults::limit_connections_outbound() + ); + + let config: Config = serde_json::from_value(json!({ + "alias": "example", + "limits": { + "connection": { + "outbound": 1337, + }, + }, + } + )) + .unwrap(); + assert_eq!( + config.limits.connection.inbound, + super::defaults::limit_connections_inbound() + ); + assert_eq!(config.limits.connection.outbound, 1337); + } }