1use std::time::Duration;
16
17use common_base::readable_size::ReadableSize;
18use common_config::{Configurable, KvBackendConfig};
19use common_event_recorder::EventRecorderOptions;
20use common_memory_manager::OnExhaustedPolicy;
21use common_options::memory::MemoryOptions;
22use common_telemetry::logging::{LoggingOptions, SlowQueryOptions, TracingOptions};
23use common_wal::config::DatanodeWalConfig;
24use datanode::config::{DatanodeOptions, ProcedureConfig, RegionEngineConfig, StorageConfig};
25use file_engine::config::EngineConfig as FileEngineConfig;
26use flow::FlowConfig;
27use frontend::frontend::FrontendOptions;
28use frontend::service_config::{
29 InfluxdbOptions, JaegerOptions, MysqlOptions, OpentsdbOptions, OtlpOptions,
30 PendingRowsBatcherOptions, PostgresOptions, PromStoreOptions, WriteRetryOptions,
31};
32use mito2::config::MitoConfig;
33use pipeline::PipelineOptions;
34use query::options::QueryOptions;
35use serde::{Deserialize, Serialize};
36use servers::grpc::GrpcOptions;
37use servers::http::HttpOptions;
38
39#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
40#[serde(default)]
41pub struct StandaloneOptions {
42 pub enable_telemetry: bool,
43 pub default_timezone: Option<String>,
44 pub default_column_prefix: Option<String>,
45 pub auto_create_table: 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 max_concurrent_queries: usize,
81 #[serde(with = "humantime_serde")]
84 pub concurrent_query_limiter_timeout: Duration,
85 pub slow_query: SlowQueryOptions,
86 pub query: QueryOptions,
87 pub memory: MemoryOptions,
88 pub pipeline: PipelineOptions,
90 pub event_recorder: EventRecorderOptions,
92 pub heartbeat_env_vars: Vec<String>,
94}
95
96impl Default for StandaloneOptions {
97 fn default() -> Self {
98 Self {
99 enable_telemetry: true,
100 default_timezone: None,
101 default_column_prefix: None,
102 auto_create_table: true,
103 max_in_flight_write_bytes: ReadableSize(0),
104 write_bytes_exhausted_policy: OnExhaustedPolicy::default(),
105 http: HttpOptions::default(),
106 grpc: GrpcOptions::default(),
107 mysql: MysqlOptions::default(),
108 postgres: PostgresOptions::default(),
109 opentsdb: OpentsdbOptions::default(),
110 influxdb: InfluxdbOptions::default(),
111 pending_rows_batcher: PendingRowsBatcherOptions::default(),
112 jaeger: JaegerOptions::default(),
113 otlp: OtlpOptions::default(),
114 prom_store: PromStoreOptions::default(),
115 wal: DatanodeWalConfig::default(),
116 storage: StorageConfig::default(),
117 metadata_store: KvBackendConfig::default(),
118 procedure: ProcedureConfig::default(),
119 flow: FlowConfig::default(),
120 logging: LoggingOptions::default(),
121 user_provider: None,
122 region_engine: vec![
123 RegionEngineConfig::Mito(MitoConfig::default()),
124 RegionEngineConfig::File(FileEngineConfig::default()),
125 ],
126 tracing: TracingOptions::default(),
127 init_regions_in_background: false,
128 init_regions_parallelism: 16,
129 max_concurrent_queries: 0,
130 concurrent_query_limiter_timeout: Duration::from_millis(100),
131 slow_query: SlowQueryOptions::default(),
132 query: QueryOptions::default(),
133 memory: MemoryOptions::default(),
134 pipeline: PipelineOptions::default(),
135 event_recorder: EventRecorderOptions::default(),
136 heartbeat_env_vars: vec![],
137 }
138 }
139}
140
141impl Configurable for StandaloneOptions {
142 fn env_list_keys() -> Option<&'static [&'static str]> {
143 Some(&[
144 "heartbeat_env_vars",
145 "wal.broker_endpoints",
146 "event_recorder.event_types",
147 "pending_rows_batcher.protocols",
148 "pending_rows_batcher.logical_table.protocols",
149 ])
150 }
151
152 fn env_tagged_seq_keys() -> Option<&'static [&'static str]> {
153 Some(&["region_engine"])
154 }
155}
156
157#[allow(clippy::from_over_into)]
161impl Into<FrontendOptions> for StandaloneOptions {
162 fn into(self) -> FrontendOptions {
163 self.frontend_options()
164 }
165}
166
167impl StandaloneOptions {
168 pub fn frontend_options(&self) -> FrontendOptions {
169 let cloned_opts = self.clone();
170 FrontendOptions {
171 default_timezone: cloned_opts.default_timezone,
172 auto_create_table: cloned_opts.auto_create_table,
173 max_in_flight_write_bytes: cloned_opts.max_in_flight_write_bytes,
174 write_bytes_exhausted_policy: cloned_opts.write_bytes_exhausted_policy,
175 http: cloned_opts.http,
176 grpc: cloned_opts.grpc,
177 mysql: cloned_opts.mysql,
178 postgres: cloned_opts.postgres,
179 opentsdb: cloned_opts.opentsdb,
180 influxdb: cloned_opts.influxdb,
181 pending_rows_batcher: cloned_opts.pending_rows_batcher,
182 jaeger: cloned_opts.jaeger,
183 otlp: cloned_opts.otlp,
184 prom_store: cloned_opts.prom_store,
185 meta_client: None,
186 write_retry: WriteRetryOptions {
188 max_retries: 0,
189 ..Default::default()
190 },
191 logging: cloned_opts.logging,
192 user_provider: cloned_opts.user_provider,
193 query: cloned_opts.query,
194 slow_query: cloned_opts.slow_query,
195 pipeline: cloned_opts.pipeline,
196 event_recorder: cloned_opts.event_recorder,
197 heartbeat_env_vars: cloned_opts.heartbeat_env_vars.clone(),
198 ..Default::default()
199 }
200 }
201
202 pub fn datanode_options(&self) -> DatanodeOptions {
203 let cloned_opts = self.clone();
204 DatanodeOptions {
205 node_id: Some(0),
206 enable_telemetry: cloned_opts.enable_telemetry,
207 wal: cloned_opts.wal,
208 storage: cloned_opts.storage,
209 region_engine: cloned_opts.region_engine,
210 grpc: cloned_opts.grpc,
211 init_regions_in_background: cloned_opts.init_regions_in_background,
212 init_regions_parallelism: cloned_opts.init_regions_parallelism,
213 max_concurrent_queries: cloned_opts.max_concurrent_queries,
214 concurrent_query_limiter_timeout: cloned_opts.concurrent_query_limiter_timeout,
215 query: cloned_opts.query,
216 heartbeat_env_vars: cloned_opts.heartbeat_env_vars,
217 ..Default::default()
218 }
219 }
220
221 pub fn sanitize(&mut self) {
223 if self.storage.is_object_storage() {
224 self.storage
225 .store
226 .cache_config_mut()
227 .unwrap()
228 .sanitize(&self.storage.data_home);
229 }
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use std::sync::Arc;
236
237 use common_event_recorder::EventTypeFilter;
238 use servers::batcher::BatchingProtocol;
239
240 use crate::options::*;
241
242 #[test]
243 fn test_concurrent_query_limiter_options() {
244 let defaults: StandaloneOptions = toml::from_str("").unwrap();
245 let datanode_defaults = DatanodeOptions::default();
246 assert_eq!(defaults.max_concurrent_queries, 0);
247 assert_eq!(
248 defaults.concurrent_query_limiter_timeout,
249 Duration::from_millis(100)
250 );
251 assert_eq!(
252 defaults.max_concurrent_queries,
253 datanode_defaults.max_concurrent_queries
254 );
255 assert_eq!(
256 defaults.concurrent_query_limiter_timeout,
257 datanode_defaults.concurrent_query_limiter_timeout
258 );
259
260 let options: StandaloneOptions = toml::from_str(
261 r#"max_concurrent_queries = 8
262concurrent_query_limiter_timeout = "250ms""#,
263 )
264 .unwrap();
265 let restored: StandaloneOptions =
266 toml::from_str(&toml::to_string(&options).unwrap()).unwrap();
267 for options in [options, restored] {
268 assert_eq!(options.max_concurrent_queries, 8);
269 assert_eq!(
270 options.concurrent_query_limiter_timeout,
271 Duration::from_millis(250)
272 );
273 let datanode = options.datanode_options();
274 assert_eq!(datanode.max_concurrent_queries, 8);
275 assert_eq!(
276 datanode.concurrent_query_limiter_timeout,
277 Duration::from_millis(250)
278 );
279 }
280 assert!(
281 toml::from_str::<StandaloneOptions>("concurrent_query_limiter_timeout = 'invalid'")
282 .is_err()
283 );
284 }
285
286 #[test]
287 fn test_concurrent_query_limiter_options_from_env() {
288 temp_env::with_vars(
289 [
290 ("STANDALONE_LIMITER_TEST__MAX_CONCURRENT_QUERIES", Some("4")),
291 (
292 "STANDALONE_LIMITER_TEST__CONCURRENT_QUERY_LIMITER_TIMEOUT",
293 Some("2s"),
294 ),
295 ],
296 || {
297 let options =
298 StandaloneOptions::load_layered_options(None, "STANDALONE_LIMITER_TEST")
299 .unwrap();
300 let datanode = options.datanode_options();
301 assert_eq!(datanode.max_concurrent_queries, 4);
302 assert_eq!(
303 datanode.concurrent_query_limiter_timeout,
304 Duration::from_secs(2)
305 );
306 },
307 );
308 }
309
310 #[test]
311 fn test_pipeline_host_access_config_forwarding() {
312 let defaults: StandaloneOptions = toml::from_str("").unwrap();
313 assert!(!defaults.pipeline.allow_vrl_host_access);
314 assert!(!defaults.frontend_options().pipeline.allow_vrl_host_access);
315 let options: StandaloneOptions =
316 toml::from_str("[pipeline]\nallow_vrl_host_access = true").unwrap();
317 assert!(options.frontend_options().pipeline.allow_vrl_host_access);
318 let serialized = toml::to_string(&options).unwrap();
319 let restored: StandaloneOptions = toml::from_str(&serialized).unwrap();
320 assert_eq!(options.pipeline, restored.pipeline);
321 }
322
323 #[test]
324 fn test_frontend_write_retries_disabled() {
325 assert!(FrontendOptions::default().write_retry.max_retries > 0);
326 assert_eq!(
327 StandaloneOptions::default()
328 .frontend_options()
329 .write_retry
330 .max_retries,
331 0
332 );
333 }
334
335 #[test]
336 fn test_logical_batcher_config_forwarding() {
337 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();
338 let frontend = opts.frontend_options();
339 assert_eq!(frontend.pending_rows_batcher, opts.pending_rows_batcher);
340 assert_eq!(
341 frontend.pending_rows_batcher.logical_table,
342 opts.pending_rows_batcher.logical_table
343 );
344 let restored: StandaloneOptions = toml::from_str(&toml::to_string(&opts).unwrap()).unwrap();
345 assert_eq!(
346 restored.pending_rows_batcher.logical_table,
347 opts.pending_rows_batcher.logical_table
348 );
349 assert!(
350 toml::from_str::<StandaloneOptions>(
351 "[pending_rows_batcher.logical_table]\nprotocols = ['logs']"
352 )
353 .is_err()
354 );
355 }
356
357 #[test]
358 fn test_logical_batcher_protocols_from_env() {
359 temp_env::with_vars(
360 [
361 (
362 "STANDALONE_LOGICAL_TEST__PENDING_ROWS_BATCHER__PROTOCOLS",
363 Some("otlp,influxdb"),
364 ),
365 (
366 "STANDALONE_LOGICAL_TEST__PENDING_ROWS_BATCHER__LOGICAL_TABLE__PROTOCOLS",
367 Some("prom,otlp"),
368 ),
369 ],
370 || {
371 let options =
372 StandaloneOptions::load_layered_options(None, "STANDALONE_LOGICAL_TEST")
373 .unwrap();
374 assert_eq!(options.pending_rows_batcher.table.protocols.len(), 2);
375 assert_eq!(
376 options
377 .pending_rows_batcher
378 .logical_table
379 .unwrap()
380 .protocols,
381 vec![BatchingProtocol::Prom, BatchingProtocol::Otlp]
382 );
383 },
384 );
385 }
386
387 #[test]
388 fn test_batcher_protocols_from_env() {
389 temp_env::with_vars(
390 [(
391 "STANDALONE_BATCHER_TEST__PENDING_ROWS_BATCHER__PROTOCOLS",
392 Some("influxdb,http_sql"),
393 )],
394 || {
395 let options =
396 StandaloneOptions::load_layered_options(None, "STANDALONE_BATCHER_TEST")
397 .unwrap();
398 assert_eq!(
399 options.pending_rows_batcher.table.protocols,
400 vec![BatchingProtocol::Influxdb, BatchingProtocol::HttpSql]
401 );
402 },
403 );
404 }
405
406 #[test]
407 fn test_protocol_pending_rows_batcher_config() {
408 let defaults: StandaloneOptions = toml::from_str("").unwrap();
409 assert!(
410 !defaults
411 .pending_rows_batcher
412 .table
413 .pending_rows_batching_enabled()
414 );
415 let options: StandaloneOptions = toml::from_str(
416 r#"
417[pending_rows_batcher]
418protocols = ["influxdb", "http_sql"]
419pending_rows_flush_interval = "5ms"
420max_batch_rows = 25
421flow_notification_queue_capacity = 17
422"#,
423 )
424 .unwrap();
425 assert_eq!(options.pending_rows_batcher.table.max_batch_rows, 25);
426 assert_eq!(options.pending_rows_batcher.table.protocols.len(), 2);
427 assert_eq!(
428 options
429 .pending_rows_batcher
430 .table
431 .flow_notification_queue_capacity
432 .get(),
433 17
434 );
435 assert!(
436 options
437 .pending_rows_batcher
438 .table
439 .pending_rows_batching_enabled()
440 );
441 let serialized = toml::to_string(&options).unwrap();
442 let parsed: StandaloneOptions = toml::from_str(&serialized).unwrap();
443 assert_eq!(options.influxdb, parsed.influxdb);
444 assert_eq!(options.opentsdb, parsed.opentsdb);
445 assert_eq!(options.pending_rows_batcher, parsed.pending_rows_batcher);
446 let frontend = options.frontend_options();
447 assert_eq!(options.influxdb, frontend.influxdb);
448 assert_eq!(options.opentsdb, frontend.opentsdb);
449 assert_eq!(options.pending_rows_batcher, frontend.pending_rows_batcher);
450 }
451
452 #[test]
453 fn test_event_recorder_event_types_preserve_filter_semantics() {
454 let all: StandaloneOptions = toml::from_str("").unwrap();
455 let none: StandaloneOptions = toml::from_str("[event_recorder]\nevent_types = []").unwrap();
456 let selected: StandaloneOptions =
457 toml::from_str("[event_recorder]\nevent_types = ['create_database']").unwrap();
458
459 assert!(all.event_recorder.event_types.allows("future_event"));
460 assert_eq!(
461 none.event_recorder.event_types.as_ref(),
462 &EventTypeFilter::Only(Default::default())
463 );
464 assert!(
465 selected
466 .event_recorder
467 .event_types
468 .allows("create_database")
469 );
470 assert!(!selected.event_recorder.event_types.allows("drop_database"));
471
472 let frontend_options = selected.frontend_options();
473 assert!(Arc::ptr_eq(
474 &selected.event_recorder.event_types,
475 &frontend_options.event_recorder.event_types,
476 ));
477 }
478
479 #[test]
480 fn test_query_options_propagated_to_components() {
481 let mut options = StandaloneOptions::default();
482 options.query.parallelism = 4;
483
484 assert_eq!(options.frontend_options().query.parallelism, 4);
485 assert_eq!(options.datanode_options().query.parallelism, 4);
486 }
487}