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
142#[allow(clippy::from_over_into)]
146impl Into<FrontendOptions> for StandaloneOptions {
147 fn into(self) -> FrontendOptions {
148 self.frontend_options()
149 }
150}
151
152impl StandaloneOptions {
153 pub fn frontend_options(&self) -> FrontendOptions {
154 let cloned_opts = self.clone();
155 FrontendOptions {
156 default_timezone: cloned_opts.default_timezone,
157 auto_create_table: cloned_opts.auto_create_table,
158 max_in_flight_write_bytes: cloned_opts.max_in_flight_write_bytes,
159 write_bytes_exhausted_policy: cloned_opts.write_bytes_exhausted_policy,
160 http: cloned_opts.http,
161 grpc: cloned_opts.grpc,
162 mysql: cloned_opts.mysql,
163 postgres: cloned_opts.postgres,
164 opentsdb: cloned_opts.opentsdb,
165 influxdb: cloned_opts.influxdb,
166 pending_rows_batcher: cloned_opts.pending_rows_batcher,
167 jaeger: cloned_opts.jaeger,
168 otlp: cloned_opts.otlp,
169 prom_store: cloned_opts.prom_store,
170 meta_client: None,
171 write_retry: WriteRetryOptions {
173 max_retries: 0,
174 ..Default::default()
175 },
176 logging: cloned_opts.logging,
177 user_provider: cloned_opts.user_provider,
178 query: cloned_opts.query,
179 slow_query: cloned_opts.slow_query,
180 pipeline: cloned_opts.pipeline,
181 event_recorder: cloned_opts.event_recorder,
182 heartbeat_env_vars: cloned_opts.heartbeat_env_vars.clone(),
183 ..Default::default()
184 }
185 }
186
187 pub fn datanode_options(&self) -> DatanodeOptions {
188 let cloned_opts = self.clone();
189 DatanodeOptions {
190 node_id: Some(0),
191 enable_telemetry: cloned_opts.enable_telemetry,
192 wal: cloned_opts.wal,
193 storage: cloned_opts.storage,
194 region_engine: cloned_opts.region_engine,
195 grpc: cloned_opts.grpc,
196 init_regions_in_background: cloned_opts.init_regions_in_background,
197 init_regions_parallelism: cloned_opts.init_regions_parallelism,
198 query: cloned_opts.query,
199 heartbeat_env_vars: cloned_opts.heartbeat_env_vars,
200 ..Default::default()
201 }
202 }
203
204 pub fn sanitize(&mut self) {
206 if self.storage.is_object_storage() {
207 self.storage
208 .store
209 .cache_config_mut()
210 .unwrap()
211 .sanitize(&self.storage.data_home);
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use std::sync::Arc;
219
220 use common_event_recorder::EventTypeFilter;
221 use servers::batcher::BatchingProtocol;
222
223 use crate::options::*;
224
225 #[test]
226 fn test_pipeline_host_access_config_forwarding() {
227 let defaults: StandaloneOptions = toml::from_str("").unwrap();
228 assert!(!defaults.pipeline.allow_vrl_host_access);
229 assert!(!defaults.frontend_options().pipeline.allow_vrl_host_access);
230 let options: StandaloneOptions =
231 toml::from_str("[pipeline]\nallow_vrl_host_access = true").unwrap();
232 assert!(options.frontend_options().pipeline.allow_vrl_host_access);
233 let serialized = toml::to_string(&options).unwrap();
234 let restored: StandaloneOptions = toml::from_str(&serialized).unwrap();
235 assert_eq!(options.pipeline, restored.pipeline);
236 }
237
238 #[test]
239 fn test_frontend_write_retries_disabled() {
240 assert!(FrontendOptions::default().write_retry.max_retries > 0);
241 assert_eq!(
242 StandaloneOptions::default()
243 .frontend_options()
244 .write_retry
245 .max_retries,
246 0
247 );
248 }
249
250 #[test]
251 fn test_logical_batcher_config_forwarding() {
252 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();
253 let frontend = opts.frontend_options();
254 assert_eq!(frontend.pending_rows_batcher, opts.pending_rows_batcher);
255 assert_eq!(
256 frontend.pending_rows_batcher.logical_table,
257 opts.pending_rows_batcher.logical_table
258 );
259 let restored: StandaloneOptions = toml::from_str(&toml::to_string(&opts).unwrap()).unwrap();
260 assert_eq!(
261 restored.pending_rows_batcher.logical_table,
262 opts.pending_rows_batcher.logical_table
263 );
264 assert!(
265 toml::from_str::<StandaloneOptions>(
266 "[pending_rows_batcher.logical_table]\nprotocols = ['logs']"
267 )
268 .is_err()
269 );
270 }
271
272 #[test]
273 fn test_logical_batcher_protocols_from_env() {
274 temp_env::with_vars(
275 [
276 (
277 "STANDALONE_LOGICAL_TEST__PENDING_ROWS_BATCHER__PROTOCOLS",
278 Some("otlp,influxdb"),
279 ),
280 (
281 "STANDALONE_LOGICAL_TEST__PENDING_ROWS_BATCHER__LOGICAL_TABLE__PROTOCOLS",
282 Some("prom,otlp"),
283 ),
284 ],
285 || {
286 let options =
287 StandaloneOptions::load_layered_options(None, "STANDALONE_LOGICAL_TEST")
288 .unwrap();
289 assert_eq!(options.pending_rows_batcher.table.protocols.len(), 2);
290 assert_eq!(
291 options
292 .pending_rows_batcher
293 .logical_table
294 .unwrap()
295 .protocols,
296 vec![BatchingProtocol::Prom, BatchingProtocol::Otlp]
297 );
298 },
299 );
300 }
301
302 #[test]
303 fn test_batcher_protocols_from_env() {
304 temp_env::with_vars(
305 [(
306 "STANDALONE_BATCHER_TEST__PENDING_ROWS_BATCHER__PROTOCOLS",
307 Some("influxdb,http_sql"),
308 )],
309 || {
310 let options =
311 StandaloneOptions::load_layered_options(None, "STANDALONE_BATCHER_TEST")
312 .unwrap();
313 assert_eq!(
314 options.pending_rows_batcher.table.protocols,
315 vec![BatchingProtocol::Influxdb, BatchingProtocol::HttpSql]
316 );
317 },
318 );
319 }
320
321 #[test]
322 fn test_protocol_pending_rows_batcher_config() {
323 let defaults: StandaloneOptions = toml::from_str("").unwrap();
324 assert!(
325 !defaults
326 .pending_rows_batcher
327 .table
328 .pending_rows_batching_enabled()
329 );
330 let options: StandaloneOptions = toml::from_str(
331 r#"
332[pending_rows_batcher]
333protocols = ["influxdb", "http_sql"]
334pending_rows_flush_interval = "5ms"
335max_batch_rows = 25
336flow_notification_queue_capacity = 17
337"#,
338 )
339 .unwrap();
340 assert_eq!(options.pending_rows_batcher.table.max_batch_rows, 25);
341 assert_eq!(options.pending_rows_batcher.table.protocols.len(), 2);
342 assert_eq!(
343 options
344 .pending_rows_batcher
345 .table
346 .flow_notification_queue_capacity
347 .get(),
348 17
349 );
350 assert!(
351 options
352 .pending_rows_batcher
353 .table
354 .pending_rows_batching_enabled()
355 );
356 let serialized = toml::to_string(&options).unwrap();
357 let parsed: StandaloneOptions = toml::from_str(&serialized).unwrap();
358 assert_eq!(options.influxdb, parsed.influxdb);
359 assert_eq!(options.opentsdb, parsed.opentsdb);
360 assert_eq!(options.pending_rows_batcher, parsed.pending_rows_batcher);
361 let frontend = options.frontend_options();
362 assert_eq!(options.influxdb, frontend.influxdb);
363 assert_eq!(options.opentsdb, frontend.opentsdb);
364 assert_eq!(options.pending_rows_batcher, frontend.pending_rows_batcher);
365 }
366
367 #[test]
368 fn test_event_recorder_event_types_preserve_filter_semantics() {
369 let all: StandaloneOptions = toml::from_str("").unwrap();
370 let none: StandaloneOptions = toml::from_str("[event_recorder]\nevent_types = []").unwrap();
371 let selected: StandaloneOptions =
372 toml::from_str("[event_recorder]\nevent_types = ['create_database']").unwrap();
373
374 assert!(all.event_recorder.event_types.allows("future_event"));
375 assert_eq!(
376 none.event_recorder.event_types.as_ref(),
377 &EventTypeFilter::Only(Default::default())
378 );
379 assert!(
380 selected
381 .event_recorder
382 .event_types
383 .allows("create_database")
384 );
385 assert!(!selected.event_recorder.event_types.allows("drop_database"));
386
387 let frontend_options = selected.frontend_options();
388 assert!(Arc::ptr_eq(
389 &selected.event_recorder.event_types,
390 &frontend_options.event_recorder.event_types,
391 ));
392 }
393
394 #[test]
395 fn test_query_options_propagated_to_components() {
396 let mut options = StandaloneOptions::default();
397 options.query.parallelism = 4;
398
399 assert_eq!(options.frontend_options().query.parallelism, 4);
400 assert_eq!(options.datanode_options().query.parallelism, 4);
401 }
402}