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