1use common_base::readable_size::ReadableSize;
16use common_config::{Configurable, KvBackendConfig};
17use common_event_recorder::EventRecorderOptions;
18use common_memory_manager::OnExhaustedPolicy;
19use common_options::memory::MemoryOptions;
20use common_telemetry::logging::{LoggingOptions, SlowQueryOptions, TracingOptions};
21use common_wal::config::DatanodeWalConfig;
22use datanode::config::{DatanodeOptions, ProcedureConfig, RegionEngineConfig, StorageConfig};
23use file_engine::config::EngineConfig as FileEngineConfig;
24use flow::FlowConfig;
25use frontend::frontend::FrontendOptions;
26use frontend::service_config::{
27 InfluxdbOptions, JaegerOptions, MysqlOptions, OpentsdbOptions, OtlpOptions,
28 PendingRowsBatcherOptions, PostgresOptions, PromStoreOptions,
29};
30use mito2::config::MitoConfig;
31use pipeline::PipelineOptions;
32use query::options::QueryOptions;
33use serde::{Deserialize, Serialize};
34use servers::grpc::GrpcOptions;
35use servers::http::HttpOptions;
36
37#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
38#[serde(default)]
39pub struct StandaloneOptions {
40 pub enable_telemetry: bool,
41 pub default_timezone: Option<String>,
42 pub default_column_prefix: Option<String>,
43 pub auto_create_table: bool,
47 pub experimental_metric_export: bool,
49 pub max_in_flight_write_bytes: ReadableSize,
52 pub write_bytes_exhausted_policy: OnExhaustedPolicy,
55 pub http: HttpOptions,
56 pub grpc: GrpcOptions,
57 pub mysql: MysqlOptions,
58 pub postgres: PostgresOptions,
59 pub opentsdb: OpentsdbOptions,
60 pub influxdb: InfluxdbOptions,
61 pub pending_rows_batcher: PendingRowsBatcherOptions,
63 pub jaeger: JaegerOptions,
64 pub otlp: OtlpOptions,
65 pub prom_store: PromStoreOptions,
66 pub wal: DatanodeWalConfig,
67 pub storage: StorageConfig,
68 pub metadata_store: KvBackendConfig,
69 pub procedure: ProcedureConfig,
70 pub flow: FlowConfig,
71 pub logging: LoggingOptions,
72 pub user_provider: Option<String>,
73 pub region_engine: Vec<RegionEngineConfig>,
75 pub tracing: TracingOptions,
76 pub init_regions_in_background: bool,
77 pub init_regions_parallelism: usize,
78 pub slow_query: SlowQueryOptions,
79 pub query: QueryOptions,
80 pub memory: MemoryOptions,
81 pub pipeline: PipelineOptions,
83 pub event_recorder: EventRecorderOptions,
85 pub heartbeat_env_vars: Vec<String>,
87}
88
89impl Default for StandaloneOptions {
90 fn default() -> Self {
91 Self {
92 enable_telemetry: true,
93 default_timezone: None,
94 default_column_prefix: None,
95 auto_create_table: true,
96 experimental_metric_export: false,
97 max_in_flight_write_bytes: ReadableSize(0),
98 write_bytes_exhausted_policy: OnExhaustedPolicy::default(),
99 http: HttpOptions::default(),
100 grpc: GrpcOptions::default(),
101 mysql: MysqlOptions::default(),
102 postgres: PostgresOptions::default(),
103 opentsdb: OpentsdbOptions::default(),
104 influxdb: InfluxdbOptions::default(),
105 pending_rows_batcher: PendingRowsBatcherOptions::default(),
106 jaeger: JaegerOptions::default(),
107 otlp: OtlpOptions::default(),
108 prom_store: PromStoreOptions::default(),
109 wal: DatanodeWalConfig::default(),
110 storage: StorageConfig::default(),
111 metadata_store: KvBackendConfig::default(),
112 procedure: ProcedureConfig::default(),
113 flow: FlowConfig::default(),
114 logging: LoggingOptions::default(),
115 user_provider: None,
116 region_engine: vec![
117 RegionEngineConfig::Mito(MitoConfig::default()),
118 RegionEngineConfig::File(FileEngineConfig::default()),
119 ],
120 tracing: TracingOptions::default(),
121 init_regions_in_background: false,
122 init_regions_parallelism: 16,
123 slow_query: SlowQueryOptions::default(),
124 query: QueryOptions::default(),
125 memory: MemoryOptions::default(),
126 pipeline: PipelineOptions::default(),
127 event_recorder: EventRecorderOptions::default(),
128 heartbeat_env_vars: vec![],
129 }
130 }
131}
132
133impl Configurable for StandaloneOptions {
134 fn env_list_keys() -> Option<&'static [&'static str]> {
135 Some(&[
136 "heartbeat_env_vars",
137 "wal.broker_endpoints",
138 "event_recorder.event_types",
139 "pending_rows_batcher.protocols",
140 "pending_rows_batcher.logical_table.protocols",
141 ])
142 }
143}
144
145#[allow(clippy::from_over_into)]
149impl Into<FrontendOptions> for StandaloneOptions {
150 fn into(self) -> FrontendOptions {
151 self.frontend_options()
152 }
153}
154
155impl StandaloneOptions {
156 pub fn frontend_options(&self) -> FrontendOptions {
157 let cloned_opts = self.clone();
158 FrontendOptions {
159 default_timezone: cloned_opts.default_timezone,
160 auto_create_table: cloned_opts.auto_create_table,
161 experimental_metric_export: cloned_opts.experimental_metric_export,
162 max_in_flight_write_bytes: cloned_opts.max_in_flight_write_bytes,
163 write_bytes_exhausted_policy: cloned_opts.write_bytes_exhausted_policy,
164 http: cloned_opts.http,
165 grpc: cloned_opts.grpc,
166 mysql: cloned_opts.mysql,
167 postgres: cloned_opts.postgres,
168 opentsdb: cloned_opts.opentsdb,
169 influxdb: cloned_opts.influxdb,
170 pending_rows_batcher: cloned_opts.pending_rows_batcher,
171 jaeger: cloned_opts.jaeger,
172 otlp: cloned_opts.otlp,
173 prom_store: cloned_opts.prom_store,
174 meta_client: None,
175 logging: cloned_opts.logging,
176 user_provider: cloned_opts.user_provider,
177 query: cloned_opts.query,
178 slow_query: cloned_opts.slow_query,
179 pipeline: cloned_opts.pipeline,
180 event_recorder: cloned_opts.event_recorder,
181 heartbeat_env_vars: cloned_opts.heartbeat_env_vars.clone(),
182 ..Default::default()
183 }
184 }
185
186 pub fn datanode_options(&self) -> DatanodeOptions {
187 let cloned_opts = self.clone();
188 DatanodeOptions {
189 node_id: Some(0),
190 enable_telemetry: cloned_opts.enable_telemetry,
191 wal: cloned_opts.wal,
192 storage: cloned_opts.storage,
193 region_engine: cloned_opts.region_engine,
194 grpc: cloned_opts.grpc,
195 init_regions_in_background: cloned_opts.init_regions_in_background,
196 init_regions_parallelism: cloned_opts.init_regions_parallelism,
197 query: cloned_opts.query,
198 heartbeat_env_vars: cloned_opts.heartbeat_env_vars,
199 ..Default::default()
200 }
201 }
202
203 pub fn sanitize(&mut self) {
205 if self.storage.is_object_storage() {
206 self.storage
207 .store
208 .cache_config_mut()
209 .unwrap()
210 .sanitize(&self.storage.data_home);
211 }
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use std::sync::Arc;
218
219 use common_event_recorder::EventTypeFilter;
220 use servers::batcher::BatchingProtocol;
221
222 use crate::options::*;
223
224 #[test]
225 fn test_logical_batcher_config_forwarding() {
226 let opts: StandaloneOptions = toml::from_str("[pending_rows_batcher]\nprotocols = ['influxdb']\n[pending_rows_batcher.logical_table]\nprotocols = ['otlp', 'prom']\npending_rows_flush_interval = '10ms'").unwrap();
227 let frontend = opts.frontend_options();
228 assert_eq!(frontend.pending_rows_batcher, opts.pending_rows_batcher);
229 assert_eq!(
230 frontend.pending_rows_batcher.logical_table,
231 opts.pending_rows_batcher.logical_table
232 );
233 let restored: StandaloneOptions = toml::from_str(&toml::to_string(&opts).unwrap()).unwrap();
234 assert_eq!(
235 restored.pending_rows_batcher.logical_table,
236 opts.pending_rows_batcher.logical_table
237 );
238 assert!(
239 toml::from_str::<StandaloneOptions>(
240 "[pending_rows_batcher.logical_table]\nprotocols = ['logs']"
241 )
242 .is_err()
243 );
244 }
245
246 #[test]
247 fn test_logical_batcher_protocols_from_env() {
248 temp_env::with_vars(
249 [
250 (
251 "STANDALONE_LOGICAL_TEST__PENDING_ROWS_BATCHER__PROTOCOLS",
252 Some("otlp,influxdb"),
253 ),
254 (
255 "STANDALONE_LOGICAL_TEST__PENDING_ROWS_BATCHER__LOGICAL_TABLE__PROTOCOLS",
256 Some("prom,otlp"),
257 ),
258 ],
259 || {
260 let options =
261 StandaloneOptions::load_layered_options(None, "STANDALONE_LOGICAL_TEST")
262 .unwrap();
263 assert_eq!(options.pending_rows_batcher.table.protocols.len(), 2);
264 assert_eq!(
265 options
266 .pending_rows_batcher
267 .logical_table
268 .unwrap()
269 .protocols,
270 vec![BatchingProtocol::Prom, BatchingProtocol::Otlp]
271 );
272 },
273 );
274 }
275
276 #[test]
277 fn test_batcher_protocols_from_env() {
278 temp_env::with_vars(
279 [(
280 "STANDALONE_BATCHER_TEST__PENDING_ROWS_BATCHER__PROTOCOLS",
281 Some("influxdb,http_sql"),
282 )],
283 || {
284 let options =
285 StandaloneOptions::load_layered_options(None, "STANDALONE_BATCHER_TEST")
286 .unwrap();
287 assert_eq!(
288 options.pending_rows_batcher.table.protocols,
289 vec![BatchingProtocol::Influxdb, BatchingProtocol::HttpSql]
290 );
291 },
292 );
293 }
294
295 #[test]
296 fn test_protocol_pending_rows_batcher_config() {
297 let defaults: StandaloneOptions = toml::from_str("").unwrap();
298 assert!(
299 !defaults
300 .pending_rows_batcher
301 .table
302 .pending_rows_batching_enabled()
303 );
304 let options: StandaloneOptions = toml::from_str(
305 r#"
306[pending_rows_batcher]
307protocols = ["influxdb", "http_sql"]
308pending_rows_flush_interval = "5ms"
309max_batch_rows = 25
310flow_notification_queue_capacity = 17
311"#,
312 )
313 .unwrap();
314 assert_eq!(options.pending_rows_batcher.table.max_batch_rows, 25);
315 assert_eq!(options.pending_rows_batcher.table.protocols.len(), 2);
316 assert_eq!(
317 options
318 .pending_rows_batcher
319 .table
320 .flow_notification_queue_capacity
321 .get(),
322 17
323 );
324 assert!(
325 options
326 .pending_rows_batcher
327 .table
328 .pending_rows_batching_enabled()
329 );
330 let serialized = toml::to_string(&options).unwrap();
331 let parsed: StandaloneOptions = toml::from_str(&serialized).unwrap();
332 assert_eq!(options.influxdb, parsed.influxdb);
333 assert_eq!(options.opentsdb, parsed.opentsdb);
334 assert_eq!(options.pending_rows_batcher, parsed.pending_rows_batcher);
335 let frontend = options.frontend_options();
336 assert_eq!(options.influxdb, frontend.influxdb);
337 assert_eq!(options.opentsdb, frontend.opentsdb);
338 assert_eq!(options.pending_rows_batcher, frontend.pending_rows_batcher);
339 }
340
341 #[test]
342 fn test_event_recorder_event_types_preserve_filter_semantics() {
343 let all: StandaloneOptions = toml::from_str("").unwrap();
344 let none: StandaloneOptions = toml::from_str("[event_recorder]\nevent_types = []").unwrap();
345 let selected: StandaloneOptions =
346 toml::from_str("[event_recorder]\nevent_types = ['create_database']").unwrap();
347
348 assert!(all.event_recorder.event_types.allows("future_event"));
349 assert_eq!(
350 none.event_recorder.event_types.as_ref(),
351 &EventTypeFilter::Only(Default::default())
352 );
353 assert!(
354 selected
355 .event_recorder
356 .event_types
357 .allows("create_database")
358 );
359 assert!(!selected.event_recorder.event_types.allows("drop_database"));
360
361 let frontend_options = selected.frontend_options();
362 assert!(Arc::ptr_eq(
363 &selected.event_recorder.event_types,
364 &frontend_options.event_recorder.event_types,
365 ));
366 }
367
368 #[test]
369 fn test_query_options_propagated_to_components() {
370 let mut options = StandaloneOptions::default();
371 assert!(!options.frontend_options().experimental_metric_export);
372 options.experimental_metric_export = true;
373 assert!(options.frontend_options().experimental_metric_export);
374 options.query.parallelism = 4;
375
376 assert_eq!(options.frontend_options().query.parallelism, 4);
377 assert_eq!(options.datanode_options().query.parallelism, 4);
378 }
379}