1use common_base::readable_size::ReadableSize;
18use common_config::{Configurable, DEFAULT_DATA_HOME};
19use common_options::memory::MemoryOptions;
20pub use common_procedure::options::ProcedureConfig;
21use common_telemetry::logging::{LoggingOptions, TracingOptions};
22use common_wal::config::DatanodeWalConfig;
23use common_workload::{DatanodeWorkloadType, sanitize_workload_types};
24use file_engine::config::EngineConfig as FileEngineConfig;
25use meta_client::MetaClientOptions;
26use metric_engine::config::EngineConfig as MetricEngineConfig;
27use mito2::config::MitoConfig;
28pub(crate) use object_store::config::ObjectStoreConfig;
29use query::options::QueryOptions;
30use serde::{Deserialize, Serialize};
31use servers::grpc::GrpcOptions;
32use servers::http::HttpOptions;
33
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
36#[serde(default)]
37pub struct StorageConfig {
38 pub data_home: String,
40 #[serde(flatten)]
41 pub store: ObjectStoreConfig,
42 pub providers: Vec<ObjectStoreConfig>,
44}
45
46impl StorageConfig {
47 pub fn is_object_storage(&self) -> bool {
49 self.store.is_object_storage()
50 }
51}
52
53impl Default for StorageConfig {
54 fn default() -> Self {
55 Self {
56 data_home: DEFAULT_DATA_HOME.to_string(),
57 store: ObjectStoreConfig::default(),
58 providers: vec![],
59 }
60 }
61}
62
63#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
64#[serde(default)]
65pub struct DatanodeOptions {
66 pub node_id: Option<u64>,
67 pub default_column_prefix: Option<String>,
68 pub workload_types: Vec<DatanodeWorkloadType>,
69 pub require_lease_before_startup: bool,
70 pub init_regions_in_background: bool,
71 pub init_regions_parallelism: usize,
72 pub grpc: GrpcOptions,
73 pub http: HttpOptions,
74 pub meta_client: Option<MetaClientOptions>,
75 pub wal: DatanodeWalConfig,
76 pub storage: StorageConfig,
77 pub max_concurrent_queries: usize,
78 pub region_engine: Vec<RegionEngineConfig>,
80 pub logging: LoggingOptions,
81 pub enable_telemetry: bool,
82 pub tracing: TracingOptions,
83 pub query: QueryOptions,
84 pub memory: MemoryOptions,
85
86 pub heartbeat_env_vars: Vec<String>,
89
90 #[deprecated(note = "Please use `grpc.bind_addr` instead.")]
92 pub rpc_addr: Option<String>,
93 #[deprecated(note = "Please use `grpc.server_addr` instead.")]
94 pub rpc_hostname: Option<String>,
95 #[deprecated(note = "Please use `grpc.runtime_size` instead.")]
96 pub rpc_runtime_size: Option<usize>,
97 #[deprecated(note = "Please use `grpc.max_recv_message_size` instead.")]
98 pub rpc_max_recv_message_size: Option<ReadableSize>,
99 #[deprecated(note = "Please use `grpc.max_send_message_size` instead.")]
100 pub rpc_max_send_message_size: Option<ReadableSize>,
101}
102
103impl DatanodeOptions {
104 pub fn sanitize(&mut self) {
106 sanitize_workload_types(&mut self.workload_types);
107
108 if self.storage.is_object_storage() {
109 self.storage
110 .store
111 .cache_config_mut()
112 .unwrap()
113 .sanitize(&self.storage.data_home);
114 }
115 }
116}
117
118impl Default for DatanodeOptions {
119 #[allow(deprecated)]
120 fn default() -> Self {
121 Self {
122 node_id: None,
123 default_column_prefix: None,
124 workload_types: vec![DatanodeWorkloadType::Hybrid],
125 require_lease_before_startup: false,
126 init_regions_in_background: false,
127 init_regions_parallelism: 16,
128 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:3001"),
129 http: HttpOptions::default(),
130 meta_client: None,
131 wal: DatanodeWalConfig::default(),
132 storage: StorageConfig::default(),
133 max_concurrent_queries: 0,
134 region_engine: vec![
135 RegionEngineConfig::Mito(MitoConfig::default()),
136 RegionEngineConfig::File(FileEngineConfig::default()),
137 ],
138 logging: LoggingOptions::default(),
139 enable_telemetry: true,
140 tracing: TracingOptions::default(),
141 query: QueryOptions::default(),
142 memory: MemoryOptions::default(),
143 heartbeat_env_vars: vec![],
144
145 rpc_addr: None,
147 rpc_hostname: None,
148 rpc_runtime_size: None,
149 rpc_max_recv_message_size: None,
150 rpc_max_send_message_size: None,
151 }
152 }
153}
154
155impl Configurable for DatanodeOptions {
156 fn env_list_keys() -> Option<&'static [&'static str]> {
157 Some(&[
158 "heartbeat_env_vars",
159 "meta_client.metasrv_addrs",
160 "wal.broker_endpoints",
161 ])
162 }
163}
164
165#[allow(clippy::large_enum_variant)]
166#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
167pub enum RegionEngineConfig {
168 #[serde(rename = "mito")]
169 Mito(MitoConfig),
170 #[serde(rename = "file")]
171 File(FileEngineConfig),
172 #[serde(rename = "metric")]
173 Metric(MetricEngineConfig),
174}
175
176#[cfg(test)]
177mod tests {
178 use common_base::secrets::ExposeSecret;
179
180 use super::*;
181
182 #[test]
183 fn test_toml() {
184 let opts = DatanodeOptions::default();
185 let toml_string = toml::to_string(&opts).unwrap();
186 let _parsed: DatanodeOptions = toml::from_str(&toml_string).unwrap();
187 }
188
189 #[test]
190 fn test_secstr() {
191 let toml_str = r#"
192 [storage]
193 type = "S3"
194 access_key_id = "access_key_id"
195 secret_access_key = "secret_access_key"
196 "#;
197 let opts: DatanodeOptions = toml::from_str(toml_str).unwrap();
198 match &opts.storage.store {
199 ObjectStoreConfig::S3(cfg) => {
200 assert_eq!(
201 "SecretBox<alloc::string::String>([REDACTED])".to_string(),
202 format!("{:?}", cfg.connection.access_key_id)
203 );
204 assert_eq!(
205 "access_key_id",
206 cfg.connection.access_key_id.expose_secret()
207 );
208 }
209 _ => unreachable!(),
210 }
211 }
212 #[test]
213 fn test_skip_ssl_validation_config() {
214 let toml_str_true = r#"
216 [storage]
217 type = "S3"
218 [storage.http_client]
219 skip_ssl_validation = true
220 "#;
221 let opts: DatanodeOptions = toml::from_str(toml_str_true).unwrap();
222 match &opts.storage.store {
223 ObjectStoreConfig::S3(cfg) => {
224 assert!(cfg.http_client.skip_ssl_validation);
225 }
226 _ => panic!("Expected S3 config"),
227 }
228
229 let toml_str_false = r#"
231 [storage]
232 type = "S3"
233 [storage.http_client]
234 skip_ssl_validation = false
235 "#;
236 let opts: DatanodeOptions = toml::from_str(toml_str_false).unwrap();
237 match &opts.storage.store {
238 ObjectStoreConfig::S3(cfg) => {
239 assert!(!cfg.http_client.skip_ssl_validation);
240 }
241 _ => panic!("Expected S3 config"),
242 }
243 let toml_str_default = r#"
245 [storage]
246 type = "S3"
247 "#;
248 let opts: DatanodeOptions = toml::from_str(toml_str_default).unwrap();
249 match &opts.storage.store {
250 ObjectStoreConfig::S3(cfg) => {
251 assert!(!cfg.http_client.skip_ssl_validation);
252 }
253 _ => panic!("Expected S3 config"),
254 }
255 }
256
257 #[test]
258 fn test_cache_config() {
259 let toml_str = r#"
260 [storage]
261 data_home = "test_data_home"
262 type = "S3"
263 [storage.cache_config]
264 enable_read_cache = true
265 "#;
266 let mut opts: DatanodeOptions = toml::from_str(toml_str).unwrap();
267 opts.sanitize();
268 assert!(opts.storage.store.cache_config().unwrap().enable_read_cache);
269 assert_eq!(
270 opts.storage.store.cache_config().unwrap().cache_path,
271 "test_data_home"
272 );
273 }
274}