d_engine/config/
cluster.rs

1use std::net::SocketAddr;
2use std::path::PathBuf;
3
4use config::ConfigError;
5use serde::Deserialize;
6use serde::Serialize;
7
8use super::validate_directory;
9use crate::proto::cluster::NodeMeta;
10use crate::proto::common::NodeStatus;
11use crate::Error;
12use crate::Result;
13use crate::FOLLOWER;
14
15/// Cluster node configuration parameters
16///
17/// Encapsulates all essential settings for cluster node initialization and operation,
18/// including network settings, storage paths, and cluster topology.
19///
20/// # Defaults
21/// Configuration can be loaded from file with default values generated via `serde`'s
22/// default implementations. Field-level defaults use helper functions prefixed with `default_`.
23
24#[derive(Debug, Serialize, Deserialize, Clone)]
25pub struct ClusterConfig {
26    /// Unique node identifier in cluster
27    ///
28    /// Default: `default_node_id()` (typically 0 for single-node setup)
29    #[serde(default = "default_node_id")]
30    pub node_id: u32,
31
32    /// Network listening address (IP:PORT)
33    ///
34    /// Default: `default_listen_addr()` (127.0.0.1:8000)
35    #[serde(default = "default_listen_addr")]
36    pub listen_address: SocketAddr,
37
38    /// Seed nodes for cluster initialization
39    ///
40    /// Default: `default_initial_cluster()` (empty vector)
41    ///
42    /// # Note
43    /// Should contain at least 3 nodes for production deployment
44    #[serde(default = "default_initial_cluster")]
45    pub initial_cluster: Vec<NodeMeta>,
46
47    /// Database storage root directory
48    ///
49    /// Default: `default_db_dir()` (/tmp/db)
50    #[serde(default = "default_db_dir")]
51    pub db_root_dir: PathBuf,
52
53    /// Log files output directory
54    ///
55    /// Default: `default_log_dir()` (./logs)
56    #[serde(default = "default_log_dir")]
57    pub log_dir: PathBuf,
58}
59impl Default for ClusterConfig {
60    fn default() -> Self {
61        Self {
62            node_id: default_node_id(),
63            listen_address: default_listen_addr(),
64            initial_cluster: vec![],
65            db_root_dir: default_db_dir(),
66            log_dir: default_log_dir(),
67        }
68    }
69}
70
71impl ClusterConfig {
72    /// Validates cluster configuration consistency
73    /// # Errors
74    /// Returns `Error::InvalidConfig` if any configuration rules are violated
75    pub fn validate(&self) -> Result<()> {
76        // Validate node identity
77        if self.node_id == 0 {
78            return Err(Error::Config(ConfigError::Message(
79                "node_id cannot be 0 (reserved for invalid nodes)".into(),
80            )));
81        }
82
83        // Validate cluster membership
84        if self.initial_cluster.is_empty() {
85            return Err(Error::Config(ConfigError::Message(
86                "initial_cluster must contain at least one node".into(),
87            )));
88        }
89
90        // Check node existence in cluster
91        let self_in_cluster = self.initial_cluster.iter().any(|n| n.id == self.node_id);
92        if !self_in_cluster {
93            return Err(Error::Config(ConfigError::Message(format!(
94                "Current node {} not found in initial_cluster",
95                self.node_id
96            ))));
97        }
98
99        // Check unique node IDs
100        let mut ids = std::collections::HashSet::new();
101        for node in &self.initial_cluster {
102            if !ids.insert(node.id) {
103                return Err(Error::Config(ConfigError::Message(format!(
104                    "Duplicate node_id {} in initial_cluster",
105                    node.id
106                ))));
107            }
108        }
109
110        // Validate network configuration
111        if self.listen_address.port() == 0 {
112            return Err(Error::Config(ConfigError::Message(
113                "listen_address must specify a non-zero port".into(),
114            )));
115        }
116
117        // Validate storage paths
118        validate_directory(&self.db_root_dir, "db_root_dir")?;
119        validate_directory(&self.log_dir, "log_dir")?;
120
121        Ok(())
122    }
123}
124
125fn default_node_id() -> u32 {
126    1
127}
128fn default_initial_cluster() -> Vec<NodeMeta> {
129    vec![NodeMeta {
130        id: 1,
131        address: "127.0.0.1:8080".to_string(),
132        role: FOLLOWER,
133        status: NodeStatus::Active.into(),
134    }]
135}
136fn default_listen_addr() -> SocketAddr {
137    "127.0.0.1:9081".parse().unwrap()
138}
139fn default_db_dir() -> PathBuf {
140    PathBuf::from("/tmp/db")
141}
142fn default_log_dir() -> PathBuf {
143    PathBuf::from("/tmp/logs")
144}