Skip to main content

standalone/
options.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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, PostgresOptions,
28    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    /// Server-side global switch for auto table creation on write.
44    /// Upper bound: when `false`, missing tables are never auto-created even if a
45    /// request sets the `auto_create_table` hint to `true`. Default: `true`.
46    pub auto_create_table: bool,
47    /// Maximum total memory for all concurrent write request bodies and messages (HTTP, gRPC, Flight).
48    /// Set to 0 to disable the limit. Default: "0" (unlimited)
49    pub max_in_flight_write_bytes: ReadableSize,
50    /// Policy when write bytes quota is exhausted.
51    /// Options: "wait" (default, 10s), "wait(<duration>)", "fail"
52    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 jaeger: JaegerOptions,
60    pub otlp: OtlpOptions,
61    pub prom_store: PromStoreOptions,
62    pub wal: DatanodeWalConfig,
63    pub storage: StorageConfig,
64    pub metadata_store: KvBackendConfig,
65    pub procedure: ProcedureConfig,
66    pub flow: FlowConfig,
67    pub logging: LoggingOptions,
68    pub user_provider: Option<String>,
69    /// Options for different store engines.
70    pub region_engine: Vec<RegionEngineConfig>,
71    pub tracing: TracingOptions,
72    pub init_regions_in_background: bool,
73    pub init_regions_parallelism: usize,
74    pub slow_query: SlowQueryOptions,
75    pub query: QueryOptions,
76    pub memory: MemoryOptions,
77    /// The pipeline options.
78    pub pipeline: PipelineOptions,
79    /// The event recorder options.
80    pub event_recorder: EventRecorderOptions,
81    /// Environment variable keys to read and report in heartbeat messages.
82    pub heartbeat_env_vars: Vec<String>,
83}
84
85impl Default for StandaloneOptions {
86    fn default() -> Self {
87        Self {
88            enable_telemetry: true,
89            default_timezone: None,
90            default_column_prefix: None,
91            auto_create_table: true,
92            max_in_flight_write_bytes: ReadableSize(0),
93            write_bytes_exhausted_policy: OnExhaustedPolicy::default(),
94            http: HttpOptions::default(),
95            grpc: GrpcOptions::default(),
96            mysql: MysqlOptions::default(),
97            postgres: PostgresOptions::default(),
98            opentsdb: OpentsdbOptions::default(),
99            influxdb: InfluxdbOptions::default(),
100            jaeger: JaegerOptions::default(),
101            otlp: OtlpOptions::default(),
102            prom_store: PromStoreOptions::default(),
103            wal: DatanodeWalConfig::default(),
104            storage: StorageConfig::default(),
105            metadata_store: KvBackendConfig::default(),
106            procedure: ProcedureConfig::default(),
107            flow: FlowConfig::default(),
108            logging: LoggingOptions::default(),
109            user_provider: None,
110            region_engine: vec![
111                RegionEngineConfig::Mito(MitoConfig::default()),
112                RegionEngineConfig::File(FileEngineConfig::default()),
113            ],
114            tracing: TracingOptions::default(),
115            init_regions_in_background: false,
116            init_regions_parallelism: 16,
117            slow_query: SlowQueryOptions::default(),
118            query: QueryOptions::default(),
119            memory: MemoryOptions::default(),
120            pipeline: PipelineOptions::default(),
121            event_recorder: EventRecorderOptions::default(),
122            heartbeat_env_vars: vec![],
123        }
124    }
125}
126
127impl Configurable for StandaloneOptions {
128    fn env_list_keys() -> Option<&'static [&'static str]> {
129        Some(&[
130            "heartbeat_env_vars",
131            "wal.broker_endpoints",
132            "event_recorder.event_types",
133        ])
134    }
135}
136
137/// The [`StandaloneOptions`] is only defined in `standalone` crate,
138/// we don't want to make `frontend` depends on it, so impl [`Into`]
139/// rather than [`From`].
140#[allow(clippy::from_over_into)]
141impl Into<FrontendOptions> for StandaloneOptions {
142    fn into(self) -> FrontendOptions {
143        self.frontend_options()
144    }
145}
146
147impl StandaloneOptions {
148    pub fn frontend_options(&self) -> FrontendOptions {
149        let cloned_opts = self.clone();
150        FrontendOptions {
151            default_timezone: cloned_opts.default_timezone,
152            auto_create_table: cloned_opts.auto_create_table,
153            max_in_flight_write_bytes: cloned_opts.max_in_flight_write_bytes,
154            write_bytes_exhausted_policy: cloned_opts.write_bytes_exhausted_policy,
155            http: cloned_opts.http,
156            grpc: cloned_opts.grpc,
157            mysql: cloned_opts.mysql,
158            postgres: cloned_opts.postgres,
159            opentsdb: cloned_opts.opentsdb,
160            influxdb: cloned_opts.influxdb,
161            jaeger: cloned_opts.jaeger,
162            otlp: cloned_opts.otlp,
163            prom_store: cloned_opts.prom_store,
164            meta_client: None,
165            logging: cloned_opts.logging,
166            user_provider: cloned_opts.user_provider,
167            query: cloned_opts.query,
168            slow_query: cloned_opts.slow_query,
169            pipeline: cloned_opts.pipeline,
170            event_recorder: cloned_opts.event_recorder,
171            heartbeat_env_vars: cloned_opts.heartbeat_env_vars.clone(),
172            ..Default::default()
173        }
174    }
175
176    pub fn datanode_options(&self) -> DatanodeOptions {
177        let cloned_opts = self.clone();
178        DatanodeOptions {
179            node_id: Some(0),
180            enable_telemetry: cloned_opts.enable_telemetry,
181            wal: cloned_opts.wal,
182            storage: cloned_opts.storage,
183            region_engine: cloned_opts.region_engine,
184            grpc: cloned_opts.grpc,
185            init_regions_in_background: cloned_opts.init_regions_in_background,
186            init_regions_parallelism: cloned_opts.init_regions_parallelism,
187            query: cloned_opts.query,
188            heartbeat_env_vars: cloned_opts.heartbeat_env_vars,
189            ..Default::default()
190        }
191    }
192
193    /// Sanitize the `StandaloneOptions` to ensure the config is valid.
194    pub fn sanitize(&mut self) {
195        if self.storage.is_object_storage() {
196            self.storage
197                .store
198                .cache_config_mut()
199                .unwrap()
200                .sanitize(&self.storage.data_home);
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use std::sync::Arc;
208
209    use common_event_recorder::EventTypeFilter;
210
211    use super::*;
212
213    #[test]
214    fn test_event_recorder_event_types_preserve_filter_semantics() {
215        let all: StandaloneOptions = toml::from_str("").unwrap();
216        let none: StandaloneOptions = toml::from_str("[event_recorder]\nevent_types = []").unwrap();
217        let selected: StandaloneOptions =
218            toml::from_str("[event_recorder]\nevent_types = ['create_database']").unwrap();
219
220        assert!(all.event_recorder.event_types.allows("future_event"));
221        assert_eq!(
222            none.event_recorder.event_types.as_ref(),
223            &EventTypeFilter::Only(Default::default())
224        );
225        assert!(
226            selected
227                .event_recorder
228                .event_types
229                .allows("create_database")
230        );
231        assert!(!selected.event_recorder.event_types.allows("drop_database"));
232
233        let frontend_options = selected.frontend_options();
234        assert!(Arc::ptr_eq(
235            &selected.event_recorder.event_types,
236            &frontend_options.event_recorder.event_types,
237        ));
238    }
239
240    #[test]
241    fn test_query_options_propagated_to_components() {
242        let mut options = StandaloneOptions::default();
243        options.query.parallelism = 4;
244
245        assert_eq!(options.frontend_options().query.parallelism, 4);
246        assert_eq!(options.datanode_options().query.parallelism, 4);
247    }
248}