diff --git a/crates/benchmark/src/runner.rs b/crates/benchmark/src/runner.rs index d60997aa6..166a6659e 100644 --- a/crates/benchmark/src/runner.rs +++ b/crates/benchmark/src/runner.rs @@ -159,15 +159,17 @@ impl CommandRunner { /// Create clients async fn create_clients(&self) -> Result> { - let client_options = ClientOptions::default().with_client_config(ClientConfig::new( - Duration::from_secs(10), - Duration::from_secs(5), - Duration::from_millis(250), - Duration::from_millis(10_000), - 3, - true, - Duration::from_secs(1), - )); + let client_options = ClientOptions::default().with_client_config( + ClientConfig::builder() + .wait_synced_timeout(Duration::from_secs(10)) + .propose_timeout(Duration::from_secs(5)) + .initial_retry_timeout(Duration::from_millis(250)) + .max_retry_timeout(Duration::from_millis(10_000)) + .retry_count(3) + .fixed_backoff(true) + .keep_alive_interval(Duration::from_secs(1)) + .build(), + ); let addrs = self .args .endpoints diff --git a/crates/utils/src/config/auth.rs b/crates/utils/src/config/auth.rs index 1c3c8e587..fbb830028 100644 --- a/crates/utils/src/config/auth.rs +++ b/crates/utils/src/config/auth.rs @@ -15,13 +15,47 @@ pub struct AuthConfig { } impl AuthConfig { - /// Generate a new `AuthConfig` object + /// Create a builder for `AuthConfig` + #[inline] #[must_use] + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Builder for `AuthConfig` +#[derive(Default, Debug)] +pub struct Builder { + /// The public key file + auth_public_key: Option, + /// The private key file + auth_private_key: Option, +} + +impl Builder { + /// Set the public key file #[inline] - pub fn new(auth_public_key: Option, auth_private_key: Option) -> Self { - Self { - auth_public_key, - auth_private_key, + #[must_use] + pub fn auth_public_key(mut self, path: Option) -> Self { + self.auth_public_key = path; + self + } + + /// Set the private key file + #[inline] + #[must_use] + pub fn auth_private_key(mut self, path: Option) -> Self { + self.auth_private_key = path; + self + } + + /// Build the `AuthConfig` + #[inline] + #[must_use] + pub fn build(self) -> AuthConfig { + AuthConfig { + auth_public_key: self.auth_public_key, + auth_private_key: self.auth_private_key, } } } diff --git a/crates/utils/src/config/client.rs b/crates/utils/src/config/client.rs index 111966298..a429ee58e 100644 --- a/crates/utils/src/config/client.rs +++ b/crates/utils/src/config/client.rs @@ -57,35 +57,11 @@ pub struct ClientConfig { } impl ClientConfig { - /// Create a new client timeout - /// - /// # Panics - /// - /// Panics if `initial_retry_timeout` is larger than `max_retry_timeout` - #[must_use] + /// Create a builder for `ClientConfig` #[inline] - pub fn new( - wait_synced_timeout: Duration, - propose_timeout: Duration, - initial_retry_timeout: Duration, - max_retry_timeout: Duration, - retry_count: usize, - fixed_backoff: bool, - keep_alive_interval: Duration, - ) -> Self { - assert!( - initial_retry_timeout <= max_retry_timeout, - "`initial_retry_timeout` should less or equal to `max_retry_timeout`" - ); - Self { - wait_synced_timeout, - propose_timeout, - initial_retry_timeout, - max_retry_timeout, - retry_count, - fixed_backoff, - keep_alive_interval, - } + #[must_use] + pub fn builder() -> Builder { + Builder::default() } } @@ -103,3 +79,115 @@ impl Default for ClientConfig { } } } + +/// Builder for `ClientConfig` +#[derive(Default, Debug, Clone, Copy)] +pub struct Builder { + /// Curp client wait sync timeout + wait_synced_timeout: Option, + /// Curp client propose request timeout + propose_timeout: Option, + /// Curp client initial retry interval + initial_retry_timeout: Option, + /// Curp client max retry interval + max_retry_timeout: Option, + /// Curp client retry interval + retry_count: Option, + /// Whether to use exponential backoff in retries + fixed_backoff: Option, + /// Curp client keep client id alive interval + keep_alive_interval: Option, +} + +impl Builder { + /// Set the wait sync timeout + #[inline] + #[must_use] + pub fn wait_synced_timeout(mut self, timeout: Duration) -> Self { + self.wait_synced_timeout = Some(timeout); + self + } + + /// Set the propose timeout + #[inline] + #[must_use] + pub fn propose_timeout(mut self, timeout: Duration) -> Self { + self.propose_timeout = Some(timeout); + self + } + + /// Set the initial retry timeout + #[inline] + #[must_use] + pub fn initial_retry_timeout(mut self, timeout: Duration) -> Self { + self.initial_retry_timeout = Some(timeout); + self + } + + /// Set the max retry timeout + #[inline] + #[must_use] + pub fn max_retry_timeout(mut self, timeout: Duration) -> Self { + self.max_retry_timeout = Some(timeout); + self + } + + /// Set the retry count + #[inline] + #[must_use] + pub fn retry_count(mut self, count: usize) -> Self { + self.retry_count = Some(count); + self + } + + /// Set whether to use fixed backoff + #[inline] + #[must_use] + pub fn fixed_backoff(mut self, use_backoff: bool) -> Self { + self.fixed_backoff = Some(use_backoff); + self + } + + /// Set the keep alive interval + #[inline] + #[must_use] + pub fn keep_alive_interval(mut self, interval: Duration) -> Self { + self.keep_alive_interval = Some(interval); + self + } + + /// # Panics + /// + /// Panics if `initial_retry_timeout` is larger than `max_retry_timeout` + /// Build the `ClientConfig` and validate it + #[inline] + #[must_use] + pub fn build(self) -> ClientConfig { + let initial_retry_timeout = self + .initial_retry_timeout + .unwrap_or_else(default_initial_retry_timeout); + let max_retry_timeout = self + .max_retry_timeout + .unwrap_or_else(default_max_retry_timeout); + + // Assert that `initial_retry_timeout <= max_retry_timeout` + assert!( + initial_retry_timeout <= max_retry_timeout, + "`initial_retry_timeout` should be less than or equal to `max_retry_timeout`" + ); + + ClientConfig { + wait_synced_timeout: self + .wait_synced_timeout + .unwrap_or_else(default_client_wait_synced_timeout), + propose_timeout: self.propose_timeout.unwrap_or_else(default_propose_timeout), + initial_retry_timeout, + max_retry_timeout, + retry_count: self.retry_count.unwrap_or_else(default_retry_count), + fixed_backoff: self.fixed_backoff.unwrap_or_else(default_fixed_backoff), + keep_alive_interval: self + .keep_alive_interval + .unwrap_or_else(default_client_id_keep_alive_interval), + } + } +} diff --git a/crates/utils/src/config/cluster.rs b/crates/utils/src/config/cluster.rs index 4b1a60c25..b3aa09db5 100644 --- a/crates/utils/src/config/cluster.rs +++ b/crates/utils/src/config/cluster.rs @@ -1,3 +1,4 @@ +use derive_builder::Builder; use getset::Getters; use serde::Deserialize; use std::collections::HashMap; @@ -6,7 +7,8 @@ use super::prelude::{ClientConfig, CurpConfig, XlineServerTimeout}; /// Cluster configuration object, including cluster relevant configuration fields #[allow(clippy::module_name_repetitions)] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Getters)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Getters, Builder)] +#[builder(pattern = "owned", default)] pub struct ClusterConfig { /// Get xline server name #[getset(get = "pub")] diff --git a/crates/utils/src/config/compact.rs b/crates/utils/src/config/compact.rs index 9def42083..ecc16cc49 100644 --- a/crates/utils/src/config/compact.rs +++ b/crates/utils/src/config/compact.rs @@ -33,18 +33,58 @@ impl Default for CompactConfig { } impl CompactConfig { - /// Create a new compact config + /// Create a builder for `CompactConfig` + #[inline] #[must_use] + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Builder for `CompactConfig` +#[derive(Default, Debug, Clone, Copy)] +pub struct Builder { + /// The max number of historical versions processed in a single compact operation + compact_batch_size: Option, + /// The interval between two compaction batches + compact_sleep_interval: Option, + /// The auto compactor config + auto_compact_config: Option, +} + +impl Builder { + /// Set the compact batch size #[inline] - pub fn new( - compact_batch_size: usize, - compact_sleep_interval: Duration, - auto_compact_config: Option, - ) -> Self { - Self { - compact_batch_size, - compact_sleep_interval, - auto_compact_config, + #[must_use] + pub fn compact_batch_size(mut self, size: usize) -> Self { + self.compact_batch_size = Some(size); + self + } + + /// Set the compact sleep interval + #[inline] + #[must_use] + pub fn compact_sleep_interval(mut self, interval: Duration) -> Self { + self.compact_sleep_interval = Some(interval); + self + } + + /// Set the auto compactor config + #[inline] + #[must_use] + pub fn auto_compact_config(mut self, config: Option) -> Self { + self.auto_compact_config = config; + self + } + + /// Build the `CompactConfig` + #[inline] + #[must_use] + pub fn build(self) -> CompactConfig { + CompactConfig { + compact_batch_size: self.compact_batch_size.unwrap_or_default(), + compact_sleep_interval: self.compact_sleep_interval.unwrap_or_default(), + auto_compact_config: self.auto_compact_config, } } } diff --git a/crates/utils/src/config/curp.rs b/crates/utils/src/config/curp.rs index 16baeca4e..d32d1a968 100644 --- a/crates/utils/src/config/curp.rs +++ b/crates/utils/src/config/curp.rs @@ -83,6 +83,15 @@ pub struct CurpConfig { pub log_entries_cap: usize, } +impl CurpConfig { + /// Create a new `CurpConfig` with a builder + #[must_use] + #[inline] + pub fn builder() -> CurpConfigBuilder { + CurpConfigBuilder::default() + } +} + /// default heartbeat interval #[must_use] #[inline] diff --git a/crates/utils/src/config/log.rs b/crates/utils/src/config/log.rs index 14426fc50..23ee3b714 100644 --- a/crates/utils/src/config/log.rs +++ b/crates/utils/src/config/log.rs @@ -64,14 +64,58 @@ pub const fn default_log_level() -> LevelConfig { } impl LogConfig { - /// Generate a new `LogConfig` object + /// Create a builder for `LogConfig` #[must_use] #[inline] - pub fn new(path: Option, rotation: RotationConfig, level: LevelConfig) -> Self { - Self { - path, - rotation, - level, + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Builder for `LogConfig` +#[derive(Default, Debug)] +pub struct Builder { + /// Log file path + path: Option, + /// Log rotation strategy + rotation: Option, + /// Log verbosity level + level: Option, +} + +impl Builder { + /// Set the log file path + #[inline] + #[must_use] + pub fn path(mut self, path: Option) -> Self { + self.path = path; + self + } + + /// Set the log rotation strategy + #[inline] + #[must_use] + pub fn rotation(mut self, rotation: RotationConfig) -> Self { + self.rotation = Some(rotation); + self + } + + /// Set the log verbosity level + #[inline] + #[must_use] + pub fn level(mut self, level: LevelConfig) -> Self { + self.level = Some(level); + self + } + + /// Build the `LogConfig` and apply defaults where needed + #[inline] + #[must_use] + pub fn build(self) -> LogConfig { + LogConfig { + path: self.path, + rotation: self.rotation.unwrap_or_else(default_rotation), + level: self.level.unwrap_or_else(default_log_level), } } } diff --git a/crates/utils/src/config/metrics.rs b/crates/utils/src/config/metrics.rs index e2a3f6435..04f78499a 100644 --- a/crates/utils/src/config/metrics.rs +++ b/crates/utils/src/config/metrics.rs @@ -50,25 +50,11 @@ pub struct MetricsConfig { } impl MetricsConfig { - /// Create a new `MetricsConfig` + /// Create a new `MetricsConfig` builder #[must_use] #[inline] - pub fn new( - enable: bool, - port: u16, - path: String, - push: bool, - push_endpoint: String, - push_protocol: PushProtocol, - ) -> Self { - Self { - enable, - port, - path, - push, - push_endpoint, - push_protocol, - } + pub fn builder() -> Builder { + Builder::default() } } @@ -86,6 +72,91 @@ impl Default for MetricsConfig { } } +/// Builder for `MetricsConfig` +#[derive(Debug, Default)] +pub struct Builder { + /// Enable or not + enable: Option, + /// The http port to expose + port: Option, + /// The http path to expose + path: Option, + /// Enable push or not + push: Option, + /// Push endpoint + push_endpoint: Option, + /// Push protocol + push_protocol: Option, +} + +impl Builder { + /// Set the `enable` flag + #[must_use] + #[inline] + pub fn enable(mut self, enable: bool) -> Self { + self.enable = Some(enable); + self + } + + /// Set the `port` + #[must_use] + #[inline] + pub fn port(mut self, port: u16) -> Self { + self.port = Some(port); + self + } + + /// Set the `path` + #[must_use] + #[inline] + pub fn path>(mut self, path: S) -> Self { + self.path = Some(path.into()); + self + } + + /// Set the `push` flag + #[must_use] + #[inline] + pub fn push(mut self, push: bool) -> Self { + self.push = Some(push); + self + } + + /// Set the `push_endpoint` + #[must_use] + #[inline] + pub fn push_endpoint>(mut self, push_endpoint: S) -> Self { + self.push_endpoint = Some(push_endpoint.into()); + self + } + + /// Set the `push_protocol` + #[must_use] + #[inline] + pub fn push_protocol(mut self, push_protocol: PushProtocol) -> Self { + self.push_protocol = Some(push_protocol); + self + } + + /// Build the `MetricsConfig` + #[must_use] + #[inline] + pub fn build(self) -> MetricsConfig { + MetricsConfig { + enable: self.enable.unwrap_or_else(default_metrics_enable), + port: self.port.unwrap_or_else(default_metrics_port), + path: self.path.unwrap_or_else(default_metrics_path), + push: self.push.unwrap_or_else(default_metrics_push), + push_endpoint: self + .push_endpoint + .unwrap_or_else(default_metrics_push_endpoint), + push_protocol: self + .push_protocol + .unwrap_or_else(default_metrics_push_protocol), + } + } +} + /// Default metrics enable #[must_use] #[inline] diff --git a/crates/utils/src/config/server.rs b/crates/utils/src/config/server.rs index 54fc6f69e..fd748f346 100644 --- a/crates/utils/src/config/server.rs +++ b/crates/utils/src/config/server.rs @@ -46,26 +46,110 @@ impl XlineServerConfig { /// Generates a new `XlineServerConfig` object #[must_use] #[inline] - #[allow(clippy::too_many_arguments)] - pub fn new( - cluster: ClusterConfig, - storage: StorageConfig, - log: LogConfig, - trace: TraceConfig, - auth: AuthConfig, - compact: CompactConfig, - tls: TlsConfig, - metrics: MetricsConfig, - ) -> Self { - Self { - cluster, - storage, - log, - trace, - auth, - compact, - tls, - metrics, + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Builder for `XlineServerConfig` +#[derive(Debug, Default)] +pub struct Builder { + /// cluster configuration object + cluster: Option, + /// xline storage configuration object + storage: Option, + /// log configuration object + log: Option, + /// trace configuration object + trace: Option, + /// auth configuration object + auth: Option, + /// compactor configuration object + compact: Option, + /// tls configuration object + tls: Option, + /// Metrics config + metrics: Option, +} + +impl Builder { + /// set the cluster object + #[must_use] + #[inline] + pub fn cluster(mut self, cluster: ClusterConfig) -> Self { + self.cluster = Some(cluster); + self + } + + /// set the storage object + #[must_use] + #[inline] + pub fn storage(mut self, storage: StorageConfig) -> Self { + self.storage = Some(storage); + self + } + + /// set the log object + #[must_use] + #[inline] + pub fn log(mut self, log: LogConfig) -> Self { + self.log = Some(log); + self + } + + /// set the trace object + #[must_use] + #[inline] + pub fn trace(mut self, trace: TraceConfig) -> Self { + self.trace = Some(trace); + self + } + + /// set the trace object + #[must_use] + #[inline] + pub fn auth(mut self, auth: AuthConfig) -> Self { + self.auth = Some(auth); + self + } + + /// set the compact object + #[must_use] + #[inline] + pub fn compact(mut self, compact: CompactConfig) -> Self { + self.compact = Some(compact); + self + } + + /// set the compact object + #[must_use] + #[inline] + pub fn tls(mut self, tls: TlsConfig) -> Self { + self.tls = Some(tls); + self + } + + /// set the compact object + #[must_use] + #[inline] + pub fn metrics(mut self, metrics: MetricsConfig) -> Self { + self.metrics = Some(metrics); + self + } + + /// Build the `XlineServerConfig` + #[must_use] + #[inline] + pub fn build(self) -> XlineServerConfig { + XlineServerConfig { + cluster: self.cluster.unwrap_or_default(), + storage: self.storage.unwrap_or_default(), + log: self.log.unwrap_or_default(), + trace: self.trace.unwrap_or_default(), + auth: self.auth.unwrap_or_default(), + compact: self.compact.unwrap_or_default(), + tls: self.tls.unwrap_or_default(), + metrics: self.metrics.unwrap_or_default(), } } } @@ -95,21 +179,11 @@ pub struct XlineServerTimeout { } impl XlineServerTimeout { - /// Create a new server timeout + /// Create a builder for `XlineServerTimeout` #[must_use] #[inline] - pub fn new( - range_retry_timeout: Duration, - compact_timeout: Duration, - sync_victims_interval: Duration, - watch_progress_notify_interval: Duration, - ) -> Self { - Self { - range_retry_timeout, - compact_timeout, - sync_victims_interval, - watch_progress_notify_interval, - } + pub fn builder() -> XlineServerTimeoutBuilder { + XlineServerTimeoutBuilder::default() } } @@ -125,6 +199,65 @@ impl Default for XlineServerTimeout { } } +/// Builder for `XlineServerTimeout` +#[derive(Debug, Default, Clone, Copy)] +pub struct XlineServerTimeoutBuilder { + /// Range request retry timeout settings + range_retry_timeout: Option, + /// Range request retry timeout settings + compact_timeout: Option, + /// Sync victims interval + sync_victims_interval: Option, + /// Watch progress notify interval settings + watch_progress_notify_interval: Option, +} + +impl XlineServerTimeoutBuilder { + /// Set the range retry timeout + #[must_use] + #[inline] + pub fn range_retry_timeout(mut self, timeout: Duration) -> Self { + self.range_retry_timeout = Some(timeout); + self + } + + /// Set the compact timeout + #[must_use] + #[inline] + pub fn compact_timeout(mut self, timeout: Duration) -> Self { + self.compact_timeout = Some(timeout); + self + } + + /// Set the sync victims interval + #[must_use] + #[inline] + pub fn sync_victims_interval(mut self, interval: Duration) -> Self { + self.sync_victims_interval = Some(interval); + self + } + + /// Set the watch progress notify interval + #[must_use] + #[inline] + pub fn watch_progress_notify_interval(mut self, interval: Duration) -> Self { + self.watch_progress_notify_interval = Some(interval); + self + } + + /// Build the `XlineServerTimeout` instance + #[must_use] + #[inline] + pub fn build(self) -> XlineServerTimeout { + XlineServerTimeout { + range_retry_timeout: self.range_retry_timeout.unwrap_or_default(), + compact_timeout: self.compact_timeout.unwrap_or_default(), + sync_victims_interval: self.sync_victims_interval.unwrap_or_default(), + watch_progress_notify_interval: self.watch_progress_notify_interval.unwrap_or_default(), + } + } +} + #[cfg(test)] mod tests { use std::{collections::HashMap, path::PathBuf, time::Duration}; @@ -214,22 +347,22 @@ mod tests { .build() .unwrap(); - let client_config = ClientConfig::new( - default_client_wait_synced_timeout(), - default_propose_timeout(), - Duration::from_secs(5), - Duration::from_secs(50), - default_retry_count(), - default_fixed_backoff(), - default_client_id_keep_alive_interval(), - ); - - let server_timeout = XlineServerTimeout::new( - Duration::from_secs(3), - Duration::from_secs(5), - Duration::from_millis(20), - Duration::from_secs(1), - ); + let client_config = ClientConfig::builder() + .wait_synced_timeout(default_client_wait_synced_timeout()) + .propose_timeout(default_propose_timeout()) + .initial_retry_timeout(Duration::from_secs(5)) + .max_retry_timeout(Duration::from_secs(50)) + .retry_count(default_retry_count()) + .fixed_backoff(default_fixed_backoff()) + .keep_alive_interval(default_client_id_keep_alive_interval()) + .build(); + + let server_timeout = XlineServerTimeout::builder() + .range_retry_timeout(Duration::from_secs(3)) + .compact_timeout(Duration::from_secs(5)) + .sync_victims_interval(Duration::from_millis(20)) + .watch_progress_notify_interval(Duration::from_secs(1)) + .build(); assert_eq!( *config.cluster(), @@ -257,44 +390,47 @@ mod tests { assert_eq!( *config.storage(), - StorageConfig::new(EngineConfig::Memory, default_quota()) + StorageConfig::builder() + .engine(EngineConfig::Memory) + .quota(default_quota()) + .build() ); assert_eq!( *config.log(), - LogConfig::new( - Some(PathBuf::from("/var/log/xline")), - RotationConfig::Daily, - LevelConfig::INFO - ) + LogConfig::builder() + .path(Some(PathBuf::from("/var/log/xline"))) + .rotation(RotationConfig::Daily) + .level(LevelConfig::INFO) + .build() ); assert_eq!( *config.trace(), - TraceConfig::new( - false, - false, - PathBuf::from("./jaeger_jsons"), - LevelConfig::INFO - ) + TraceConfig::builder() + .jaeger_online(false) + .jaeger_offline(false) + .jaeger_output_dir(PathBuf::from("./jaeger_jsons")) + .jaeger_level(LevelConfig::INFO) + .build() ); assert_eq!( *config.compact(), - CompactConfig::new( - 123, - Duration::from_millis(5), - Some(AutoCompactConfig::Periodic(Duration::from_secs( + CompactConfig::builder() + .compact_batch_size(123) + .compact_sleep_interval(Duration::from_millis(5)) + .auto_compact_config(Some(AutoCompactConfig::Periodic(Duration::from_secs( 10 * 60 * 60 - ))) - ) + )))) + .build() ); assert_eq!( *config.auth(), - AuthConfig::new( - Some(PathBuf::from("./public_key.pem")), - Some(PathBuf::from("./private_key.pem")) - ) + AuthConfig::builder() + .auth_public_key(Some(PathBuf::from("./public_key.pem"))) + .auth_private_key(Some(PathBuf::from("./private_key.pem"))) + .build() ); assert_eq!( @@ -309,14 +445,14 @@ mod tests { assert_eq!( *config.metrics(), - MetricsConfig::new( - true, - 9100, - "/metrics".to_owned(), - true, - "http://some-endpoint.com:4396".to_owned(), - PushProtocol::HTTP - ), + MetricsConfig::builder() + .enable(true) + .port(9100) + .path("/metrics".to_owned()) + .push(true) + .push_endpoint("http://some-endpoint.com:4396".to_owned()) + .push_protocol(PushProtocol::HTTP) + .build(), ); } @@ -388,20 +524,20 @@ mod tests { assert_eq!( *config.log(), - LogConfig::new( - Some(PathBuf::from("/var/log/xline")), - RotationConfig::Never, - LevelConfig::INFO - ) + LogConfig::builder() + .path(Some(PathBuf::from("/var/log/xline"))) + .rotation(RotationConfig::Never) + .level(LevelConfig::INFO) + .build() ); assert_eq!( *config.trace(), - TraceConfig::new( - false, - false, - PathBuf::from("./jaeger_jsons"), - LevelConfig::INFO - ) + TraceConfig::builder() + .jaeger_online(false) + .jaeger_offline(false) + .jaeger_output_dir(PathBuf::from("./jaeger_jsons")) + .jaeger_level(LevelConfig::INFO) + .build() ); assert_eq!(*config.compact(), CompactConfig::default()); assert_eq!(*config.auth(), AuthConfig::default()); @@ -454,11 +590,11 @@ mod tests { assert_eq!( *config.compact(), - CompactConfig::new( - default_compact_batch_size(), - default_compact_sleep_interval(), - Some(AutoCompactConfig::Revision(10000)) - ) + CompactConfig::builder() + .compact_batch_size(default_compact_batch_size()) + .compact_sleep_interval(default_compact_sleep_interval()) + .auto_compact_config(Some(AutoCompactConfig::Revision(10000))) + .build() ); } } diff --git a/crates/utils/src/config/storage.rs b/crates/utils/src/config/storage.rs index d0ebf45b7..55382ec5a 100644 --- a/crates/utils/src/config/storage.rs +++ b/crates/utils/src/config/storage.rs @@ -19,11 +19,11 @@ pub struct StorageConfig { } impl StorageConfig { - /// Create a new storage config + /// Create a builder for `StorageConfig` #[inline] #[must_use] - pub fn new(engine: EngineConfig, quota: u64) -> Self { - Self { engine, quota } + pub fn builder() -> Builder { + Builder::default() } } @@ -37,6 +37,43 @@ impl Default for StorageConfig { } } +/// Builder for `StorageConfig` +#[derive(Default, Debug)] +pub struct Builder { + /// Engine Configuration + engine: Option, + /// Quota + quota: Option, +} + +impl Builder { + /// Set the engine configuration + #[inline] + #[must_use] + pub fn engine(mut self, engine: EngineConfig) -> Self { + self.engine = Some(engine); + self + } + + /// Set the quota + #[inline] + #[must_use] + pub fn quota(mut self, quota: u64) -> Self { + self.quota = Some(quota); + self + } + + /// Build the `StorageConfig` and apply defaults where needed + #[inline] + #[must_use] + pub fn build(self) -> StorageConfig { + StorageConfig { + engine: self.engine.unwrap_or_default(), + quota: self.quota.unwrap_or_else(default_quota), + } + } +} + /// Default quota: 8GB #[inline] #[must_use] diff --git a/crates/utils/src/config/tls.rs b/crates/utils/src/config/tls.rs index de56d1bc3..d4e134e4b 100644 --- a/crates/utils/src/config/tls.rs +++ b/crates/utils/src/config/tls.rs @@ -29,25 +29,11 @@ pub struct TlsConfig { } impl TlsConfig { - /// Create a new `TlsConfig` object - #[must_use] - #[inline] - pub fn new( - peer_ca_cert_path: Option, - peer_cert_path: Option, - peer_key_path: Option, - client_ca_cert_path: Option, - client_cert_path: Option, - client_key_path: Option, - ) -> Self { - Self { - peer_ca_cert_path, - peer_cert_path, - peer_key_path, - client_ca_cert_path, - client_cert_path, - client_key_path, - } + /// Create a builder for `TlsConfig` + #[inline] + #[must_use] + pub fn builder() -> Builder { + Builder::default() } /// Whether the server tls is enabled @@ -57,3 +43,84 @@ impl TlsConfig { self.peer_cert_path.is_some() && self.peer_key_path.is_some() } } + +/// Builder for `TlsConfig` +#[derive(Default, Debug)] +pub struct Builder { + /// The CA certificate file used by peer to verify client certificates + peer_ca_cert_path: Option, + /// The public key file used by peer + peer_cert_path: Option, + /// The private key file used by peer + peer_key_path: Option, + /// The CA certificate file used by client to verify peer c + client_ca_cert_path: Option, + /// The public key file used by client + client_cert_path: Option, + /// The private key file used by client + client_key_path: Option, +} + +impl Builder { + /// Set the `peer_ca_cert_path` + #[inline] + #[must_use] + pub fn peer_ca_cert_path(mut self, path: Option) -> Self { + self.peer_ca_cert_path = path; + self + } + + /// Set the `peer_cert_path` + #[inline] + #[must_use] + pub fn peer_cert_path(mut self, path: Option) -> Self { + self.peer_cert_path = path; + self + } + + /// Set the `peer_key_path` + #[inline] + #[must_use] + pub fn peer_key_path(mut self, path: Option) -> Self { + self.peer_key_path = path; + self + } + + /// Set the `client_ca_cert_path` + #[inline] + #[must_use] + pub fn client_ca_cert_path(mut self, path: Option) -> Self { + self.client_ca_cert_path = path; + self + } + + /// Set the `client_cert_path` + #[inline] + #[must_use] + pub fn client_cert_path(mut self, path: Option) -> Self { + self.client_cert_path = path; + self + } + + /// Set the `client_key_path` + #[inline] + #[must_use] + pub fn client_key_path(mut self, path: Option) -> Self { + self.client_key_path = path; + self + } + + /// Build the `TlsConfig` object + #[inline] + #[must_use] + pub fn build(self) -> TlsConfig { + TlsConfig { + peer_ca_cert_path: self.peer_ca_cert_path, + peer_cert_path: self.peer_cert_path, + peer_key_path: self.peer_key_path, + client_ca_cert_path: self.client_ca_cert_path, + client_cert_path: self.client_cert_path, + client_key_path: self.client_key_path, + } + } +} diff --git a/crates/utils/src/config/trace.rs b/crates/utils/src/config/trace.rs index 935f366ce..76ca45b7b 100644 --- a/crates/utils/src/config/trace.rs +++ b/crates/utils/src/config/trace.rs @@ -37,20 +37,69 @@ impl Default for TraceConfig { } impl TraceConfig { - /// Generate a new `TraceConfig` object + /// Create a builder for `TraceConfig` + #[inline] #[must_use] + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Builder for `TraceConfig` +#[derive(Default, Debug)] +pub struct Builder { + /// Open jaeger online, sending data to jaeger agent directly + jaeger_online: Option, + /// Open jaeger offline, saving data to the `jaeger_output_dir` + jaeger_offline: Option, + /// The dir path to save the data when `jaeger_offline` is on + jaeger_output_dir: Option, + /// The verbosity level of tracing + jaeger_level: Option, +} + +impl Builder { + /// Set `jaeger_online` #[inline] - pub fn new( - jaeger_online: bool, - jaeger_offline: bool, - jaeger_output_dir: PathBuf, - jaeger_level: LevelConfig, - ) -> Self { - Self { - jaeger_online, - jaeger_offline, - jaeger_output_dir, - jaeger_level, + #[must_use] + pub fn jaeger_online(mut self, value: bool) -> Self { + self.jaeger_online = Some(value); + self + } + + /// Set `jaeger_offline` + #[inline] + #[must_use] + pub fn jaeger_offline(mut self, value: bool) -> Self { + self.jaeger_offline = Some(value); + self + } + + /// Set `jaeger_output_dir` + #[inline] + #[must_use] + pub fn jaeger_output_dir(mut self, value: PathBuf) -> Self { + self.jaeger_output_dir = Some(value); + self + } + + /// Set `jaeger_level` + #[inline] + #[must_use] + pub fn jaeger_level(mut self, value: LevelConfig) -> Self { + self.jaeger_level = Some(value); + self + } + + /// Build the `TraceConfig` object + #[inline] + #[must_use] + pub fn build(self) -> TraceConfig { + TraceConfig { + jaeger_online: self.jaeger_online.unwrap_or(false), + jaeger_offline: self.jaeger_offline.unwrap_or(false), + jaeger_output_dir: self.jaeger_output_dir.unwrap_or_else(|| "".into()), + jaeger_level: self.jaeger_level.unwrap_or_else(default_log_level), } } } diff --git a/crates/xline-test-utils/src/lib.rs b/crates/xline-test-utils/src/lib.rs index d86a3a3fb..2741a56e0 100644 --- a/crates/xline-test-utils/src/lib.rs +++ b/crates/xline-test-utils/src/lib.rs @@ -241,14 +241,26 @@ impl Cluster { quota: u64, ) -> XlineServerConfig { let cluster = ClusterConfig::default(); - let storage = StorageConfig::new(EngineConfig::RocksDB(path), quota); + let storage = StorageConfig::builder() + .engine(EngineConfig::RocksDB(path)) + .quota(quota) + .build(); let log = LogConfig::default(); let trace = TraceConfig::default(); let auth = AuthConfig::default(); let compact = CompactConfig::default(); let tls = TlsConfig::default(); let metrics = MetricsConfig::default(); - XlineServerConfig::new(cluster, storage, log, trace, auth, compact, tls, metrics) + XlineServerConfig::builder() + .cluster(cluster) + .storage(storage) + .log(log) + .trace(trace) + .auth(auth) + .compact(compact) + .tls(tls) + .metrics(metrics) + .build() } pub fn default_rocks_config_with_path(path: PathBuf) -> XlineServerConfig { @@ -288,16 +300,16 @@ impl Cluster { *old_cluster.server_timeout(), initial_cluster_state, ); - XlineServerConfig::new( - new_cluster, - base_config.storage().clone(), - base_config.log().clone(), - base_config.trace().clone(), - base_config.auth().clone(), - *base_config.compact(), - base_config.tls().clone(), - base_config.metrics().clone(), - ) + XlineServerConfig::builder() + .cluster(new_cluster) + .storage(base_config.storage().clone()) + .log(base_config.log().clone()) + .trace(base_config.trace().clone()) + .auth(base_config.auth().clone()) + .compact(*base_config.compact()) + .tls(base_config.tls().clone()) + .metrics(base_config.metrics().clone()) + .build() } } diff --git a/crates/xline/src/utils/args.rs b/crates/xline/src/utils/args.rs index 5a9da207f..b901811e0 100644 --- a/crates/xline/src/utils/args.rs +++ b/crates/xline/src/utils/args.rs @@ -233,7 +233,10 @@ impl From for XlineServerConfig { &_ => unreachable!("xline only supports memory and rocksdb engine"), }; - let storage = StorageConfig::new(engine, args.quota.unwrap_or_else(default_quota)); + let storage = StorageConfig::builder() + .engine(engine) + .quota(args.quota.unwrap_or_else(default_quota)) + .build(); let Ok(curp_config) = CurpConfigBuilder::default() .heartbeat_interval( args.heartbeat_interval @@ -255,29 +258,48 @@ impl From for XlineServerConfig { else { panic!("failed to create curp config") }; - let client_config = ClientConfig::new( - args.client_wait_synced_timeout - .unwrap_or_else(default_client_wait_synced_timeout), - args.client_propose_timeout - .unwrap_or_else(default_propose_timeout), - args.client_initial_retry_timeout - .unwrap_or_else(default_initial_retry_timeout), - args.client_max_retry_timeout - .unwrap_or_else(default_max_retry_timeout), - args.retry_count.unwrap_or_else(default_retry_count), - args.client_fixed_backoff, - args.client_keep_alive_interval - .unwrap_or_else(default_client_id_keep_alive_interval), - ); - let server_timeout = XlineServerTimeout::new( - args.range_retry_timeout - .unwrap_or_else(default_range_retry_timeout), - args.compact_timeout.unwrap_or_else(default_compact_timeout), - args.sync_victims_interval - .unwrap_or_else(default_sync_victims_interval), - args.watch_progress_notify_interval - .unwrap_or_else(default_watch_progress_notify_interval), - ); + + let client_config = ClientConfig::builder() + .wait_synced_timeout( + args.client_wait_synced_timeout + .unwrap_or_else(default_client_wait_synced_timeout), + ) + .propose_timeout( + args.client_propose_timeout + .unwrap_or_else(default_propose_timeout), + ) + .initial_retry_timeout( + args.client_initial_retry_timeout + .unwrap_or_else(default_initial_retry_timeout), + ) + .max_retry_timeout( + args.client_max_retry_timeout + .unwrap_or_else(default_max_retry_timeout), + ) + .retry_count(args.retry_count.unwrap_or_else(default_retry_count)) + .fixed_backoff(args.client_fixed_backoff) + .keep_alive_interval( + args.client_keep_alive_interval + .unwrap_or_else(default_client_id_keep_alive_interval), + ) + .build(); + + let server_timeout = XlineServerTimeout::builder() + .range_retry_timeout( + args.range_retry_timeout + .unwrap_or_else(default_range_retry_timeout), + ) + .compact_timeout(args.compact_timeout.unwrap_or_else(default_compact_timeout)) + .sync_victims_interval( + args.sync_victims_interval + .unwrap_or_else(default_sync_victims_interval), + ) + .watch_progress_notify_interval( + args.watch_progress_notify_interval + .unwrap_or_else(default_watch_progress_notify_interval), + ) + .build(); + let initial_cluster_state = args.initial_cluster_state.unwrap_or_default(); let cluster = ClusterConfig::new( args.name, @@ -292,14 +314,22 @@ impl From for XlineServerConfig { server_timeout, initial_cluster_state, ); - let log = LogConfig::new(args.log_file, args.log_rotate, args.log_level); - let trace = TraceConfig::new( - args.jaeger_online, - args.jaeger_offline, - args.jaeger_output_dir, - args.jaeger_level, - ); - let auth = AuthConfig::new(args.auth_public_key, args.auth_private_key); + let log = LogConfig::builder() + .path(args.log_file) + .rotation(args.log_rotate) + .level(args.log_level) + .build(); + + let trace = TraceConfig::builder() + .jaeger_online(args.jaeger_online) + .jaeger_offline(args.jaeger_offline) + .jaeger_output_dir(args.jaeger_output_dir) + .jaeger_level(args.jaeger_level) + .build(); + let auth = AuthConfig::builder() + .auth_public_key(args.auth_public_key) + .auth_private_key(args.auth_private_key) + .build(); let auto_compactor_cfg = if let Some(mode) = args.auto_compact_mode { match mode.as_str() { "periodic" => { @@ -321,29 +351,42 @@ impl From for XlineServerConfig { } else { None }; - let compact = CompactConfig::new( - args.compact_batch_size, - args.compact_sleep_interval - .unwrap_or_else(default_compact_sleep_interval), - auto_compactor_cfg, - ); - let tls = TlsConfig::new( - args.peer_ca_cert_path, - args.peer_cert_path, - args.peer_key_path, - args.client_ca_cert_path, - args.client_cert_path, - args.client_key_path, - ); - let metrics = MetricsConfig::new( - args.metrics_enable, - args.metrics_port, - args.metrics_path, - args.metrics_push, - args.metrics_push_endpoint, - args.metrics_push_protocol, - ); - XlineServerConfig::new(cluster, storage, log, trace, auth, compact, tls, metrics) + let compact = CompactConfig::builder() + .compact_batch_size(args.compact_batch_size) + .compact_sleep_interval( + args.compact_sleep_interval + .unwrap_or_else(default_compact_sleep_interval), + ) + .auto_compact_config(auto_compactor_cfg) + .build(); + let tls = TlsConfig::builder() + .peer_ca_cert_path(args.peer_ca_cert_path) + .peer_cert_path(args.peer_cert_path) + .peer_key_path(args.peer_key_path) + .client_ca_cert_path(args.client_ca_cert_path) + .client_cert_path(args.client_cert_path) + .client_key_path(args.client_key_path) + .build(); + + let metrics = MetricsConfig::builder() + .enable(args.metrics_enable) + .port(args.metrics_port) + .path(args.metrics_path) + .push(args.metrics_push) + .push_endpoint(args.metrics_push_endpoint) + .push_protocol(args.metrics_push_protocol) + .build(); + + XlineServerConfig::builder() + .cluster(cluster) + .storage(storage) + .log(log) + .trace(trace) + .auth(auth) + .compact(compact) + .tls(tls) + .metrics(metrics) + .build() } } diff --git a/crates/xline/tests/it/auth_test.rs b/crates/xline/tests/it/auth_test.rs index ddba12298..476372d44 100644 --- a/crates/xline/tests/it/auth_test.rs +++ b/crates/xline/tests/it/auth_test.rs @@ -217,16 +217,21 @@ fn configs_with_auth(size: usize) -> Vec { ) }) .map(|(auth_public_key, auth_private_key)| { - XlineServerConfig::new( - ClusterConfig::default(), - StorageConfig::default(), - LogConfig::default(), - TraceConfig::default(), - AuthConfig::new(auth_public_key, auth_private_key), - CompactConfig::default(), - TlsConfig::default(), - MetricsConfig::default(), - ) + XlineServerConfig::builder() + .cluster(ClusterConfig::default()) + .storage(StorageConfig::default()) + .log(LogConfig::default()) + .trace(TraceConfig::default()) + .auth( + AuthConfig::builder() + .auth_public_key(auth_public_key) + .auth_private_key(auth_private_key) + .build(), + ) + .compact(CompactConfig::default()) + .tls(TlsConfig::default()) + .metrics(MetricsConfig::default()) + .build() }) .take(size) .collect() diff --git a/crates/xline/tests/it/tls_test.rs b/crates/xline/tests/it/tls_test.rs index d1024ce65..5e8f1a01c 100644 --- a/crates/xline/tests/it/tls_test.rs +++ b/crates/xline/tests/it/tls_test.rs @@ -77,16 +77,16 @@ async fn test_certificate_authenticate() { fn configs_with_tls_config(size: usize, tls_config: TlsConfig) -> Vec { iter::repeat(tls_config) .map(|tls_config| { - XlineServerConfig::new( - ClusterConfig::default(), - StorageConfig::default(), - LogConfig::default(), - TraceConfig::default(), - AuthConfig::default(), - CompactConfig::default(), - tls_config, - MetricsConfig::default(), - ) + XlineServerConfig::builder() + .cluster(ClusterConfig::default()) + .storage(StorageConfig::default()) + .log(LogConfig::default()) + .trace(TraceConfig::default()) + .auth(AuthConfig::default()) + .compact(CompactConfig::default()) + .tls(tls_config) + .metrics(MetricsConfig::default()) + .build() }) .take(size) .collect() @@ -101,14 +101,14 @@ fn basic_tls_client_config() -> ClientTlsConfig { fn basic_tls_configs(size: usize) -> Vec { configs_with_tls_config( size, - TlsConfig::new( - None, - Some(PathBuf::from("../../fixtures/server.crt")), - Some(PathBuf::from("../../fixtures/server.key")), - Some(PathBuf::from("../../fixtures/ca.crt")), - None, - None, - ), + TlsConfig::builder() + .peer_ca_cert_path(None) + .peer_cert_path(Some(PathBuf::from("../../fixtures/server.crt"))) + .peer_key_path(Some(PathBuf::from("../../fixtures/server.key"))) + .peer_ca_cert_path(Some(PathBuf::from("../../fixtures/ca.crt"))) + .client_cert_path(None) + .client_key_path(None) + .build(), ) } @@ -126,13 +126,13 @@ fn mtls_client_config(name: &str) -> ClientTlsConfig { fn mtls_configs(size: usize) -> Vec { configs_with_tls_config( size, - TlsConfig::new( - Some(PathBuf::from("../../fixtures/ca.crt")), - Some(PathBuf::from("../../fixtures/server.crt")), - Some(PathBuf::from("../../fixtures/server.key")), - Some(PathBuf::from("../../fixtures/ca.crt")), - Some(PathBuf::from("../../fixtures/root_client.crt")), - Some(PathBuf::from("../../fixtures/root_client.key")), - ), + TlsConfig::builder() + .peer_ca_cert_path(Some(PathBuf::from("../../fixtures/ca.crt"))) + .peer_cert_path(Some(PathBuf::from("../../fixtures/server.crt"))) + .peer_key_path(Some(PathBuf::from("../../fixtures/server.key"))) + .client_ca_cert_path(Some(PathBuf::from("../../fixtures/ca.crt"))) + .client_cert_path(Some(PathBuf::from("../../fixtures/root_client.crt"))) + .client_key_path(Some(PathBuf::from("../../fixtures/root_client.key"))) + .build(), ) } diff --git a/crates/xlinectl/src/main.rs b/crates/xlinectl/src/main.rs index 4eaea1928..52d16fd6d 100644 --- a/crates/xlinectl/src/main.rs +++ b/crates/xlinectl/src/main.rs @@ -268,15 +268,27 @@ async fn main() -> Result<()> { let matches = cli().get_matches(); let user_opt = parse_user(&matches)?; let endpoints = matches.get_many::("endpoints").expect("Required"); - let client_config = ClientConfig::new( - Duration::from_secs(*matches.get_one("wait_synced_timeout").expect("Required")), - Duration::from_secs(*matches.get_one("propose_timeout").expect("Required")), - Duration::from_millis(*matches.get_one("initial_retry_timeout").expect("Required")), - Duration::from_millis(*matches.get_one("max_retry_timeout").expect("Required")), - *matches.get_one("retry_count").expect("Required"), - true, - Duration::from_millis(*matches.get_one("keep_alive_interval").expect("Required")), - ); + + let client_config = ClientConfig::builder() + .wait_synced_timeout(Duration::from_secs( + *matches.get_one("wait_synced_timeout").expect("Required"), + )) + .propose_timeout(Duration::from_secs( + *matches.get_one("propose_timeout").expect("Required"), + )) + .initial_retry_timeout(Duration::from_millis( + *matches.get_one("initial_retry_timeout").expect("Required"), + )) + .max_retry_timeout(Duration::from_millis( + *matches.get_one("max_retry_timeout").expect("Required"), + )) + .retry_count(*matches.get_one("retry_count").expect("Required")) + .fixed_backoff(true) + .keep_alive_interval(Duration::from_millis( + *matches.get_one("keep_alive_interval").expect("Required"), + )) + .build(); + let ca_path: Option = matches.get_one("ca_cert_pem_path").cloned(); let tls_config = match ca_path { Some(path) => {