Skip to main content

risedev/
service_config.rs

1// Copyright 2022 RisingWave Labs
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 serde::{Deserialize, Serialize};
16
17#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "kebab-case")]
19#[serde(deny_unknown_fields)]
20pub struct ComputeNodeConfig {
21    #[serde(rename = "use")]
22    phantom_use: Option<String>,
23    pub id: String,
24
25    pub address: String,
26    #[serde(with = "string")]
27    pub port: u16,
28    pub listen_address: String,
29    pub exporter_port: u16,
30    pub async_stack_trace: String,
31    pub enable_tiered_cache: bool,
32
33    pub provide_minio: Option<Vec<MinioConfig>>,
34    pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
35    pub provide_compute_node: Option<Vec<ComputeNodeConfig>>,
36    pub provide_opendal: Option<Vec<OpendalConfig>>,
37    pub provide_aws_s3: Option<Vec<AwsS3Config>>,
38    pub provide_moat: Option<Vec<MoatConfig>>,
39    pub provide_tempo: Option<Vec<TempoConfig>>,
40    pub user_managed: bool,
41    pub resource_group: String,
42
43    pub total_memory_bytes: usize,
44    pub parallelism: usize,
45    pub role: String,
46}
47
48#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
49#[serde(rename_all = "kebab-case")]
50#[serde(deny_unknown_fields)]
51pub enum MetaBackend {
52    Memory,
53    Sqlite,
54    Postgres,
55    Mysql,
56    Env,
57}
58
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60#[serde(rename_all = "kebab-case")]
61#[serde(deny_unknown_fields)]
62pub struct MetaNodeConfig {
63    #[serde(rename = "use")]
64    phantom_use: Option<String>,
65    pub id: String,
66
67    pub address: String,
68    #[serde(with = "string")]
69    pub port: u16,
70    pub listen_address: String,
71    pub dashboard_port: u16,
72    pub exporter_port: u16,
73
74    pub user_managed: bool,
75
76    pub meta_backend: MetaBackend,
77    pub provide_sqlite_backend: Option<Vec<SqliteConfig>>,
78    pub provide_postgres_backend: Option<Vec<PostgresConfig>>,
79    pub provide_mysql_backend: Option<Vec<MySqlConfig>>,
80    pub provide_prometheus: Option<Vec<PrometheusConfig>>,
81
82    pub provide_compute_node: Option<Vec<ComputeNodeConfig>>,
83    pub provide_compactor: Option<Vec<CompactorConfig>>,
84
85    pub provide_tempo: Option<Vec<TempoConfig>>,
86
87    pub provide_aws_s3: Option<Vec<AwsS3Config>>,
88    pub provide_minio: Option<Vec<MinioConfig>>,
89    pub provide_opendal: Option<Vec<OpendalConfig>>,
90    pub provide_moat: Option<Vec<MoatConfig>>,
91}
92
93#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
94#[serde(rename_all = "kebab-case")]
95#[serde(deny_unknown_fields)]
96pub struct FrontendConfig {
97    #[serde(rename = "use")]
98    phantom_use: Option<String>,
99    pub id: String,
100
101    pub address: String,
102    #[serde(with = "string")]
103    pub port: u16,
104    pub listen_address: String,
105    pub exporter_port: u16,
106    pub health_check_port: u16,
107
108    pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
109    pub provide_tempo: Option<Vec<TempoConfig>>,
110    pub provide_prometheus: Option<Vec<PrometheusConfig>>,
111
112    pub user_managed: bool,
113}
114
115#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
116#[serde(rename_all = "kebab-case")]
117#[serde(deny_unknown_fields)]
118pub struct CompactorConfig {
119    #[serde(rename = "use")]
120    phantom_use: Option<String>,
121    pub id: String,
122
123    pub address: String,
124    #[serde(with = "string")]
125    pub port: u16,
126    pub listen_address: String,
127    pub exporter_port: u16,
128
129    pub provide_minio: Option<Vec<MinioConfig>>,
130
131    pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
132    pub provide_tempo: Option<Vec<TempoConfig>>,
133
134    pub user_managed: bool,
135    pub compaction_worker_threads_number: Option<usize>,
136
137    pub compactor_mode: String,
138}
139
140#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
141#[serde(rename_all = "kebab-case")]
142#[serde(deny_unknown_fields)]
143pub struct MinioConfig {
144    #[serde(rename = "use")]
145    phantom_use: Option<String>,
146    pub id: String,
147
148    pub address: String,
149    #[serde(with = "string")]
150    pub port: u16,
151    pub listen_address: String,
152
153    pub console_address: String,
154    #[serde(with = "string")]
155    pub console_port: u16,
156
157    pub root_user: String,
158    pub root_password: String,
159    pub hummock_bucket: String,
160
161    pub provide_prometheus: Option<Vec<PrometheusConfig>>,
162
163    // For rate limiting minio in a test environment.
164    pub api_requests_max: usize,
165    pub api_requests_deadline: String,
166}
167
168#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
169#[serde(rename_all = "kebab-case")]
170#[serde(deny_unknown_fields)]
171pub struct SqliteConfig {
172    #[serde(rename = "use")]
173    phantom_use: Option<String>,
174    pub id: String,
175
176    pub file: String,
177}
178
179#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
180#[serde(rename_all = "kebab-case")]
181#[serde(deny_unknown_fields)]
182pub struct PrometheusConfig {
183    #[serde(rename = "use")]
184    phantom_use: Option<String>,
185    pub id: String,
186
187    pub address: String,
188    #[serde(with = "string")]
189    pub port: u16,
190    pub listen_address: String,
191
192    pub remote_write: bool,
193    pub remote_write_region: String,
194    pub remote_write_url: String,
195
196    pub scrape_interval: String,
197
198    pub provide_compute_node: Option<Vec<ComputeNodeConfig>>,
199    pub provide_meta_node: Option<Vec<MetaNodeConfig>>,
200    pub provide_minio: Option<Vec<MinioConfig>>,
201    pub provide_compactor: Option<Vec<CompactorConfig>>,
202    pub provide_frontend: Option<Vec<FrontendConfig>>,
203}
204
205#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
206#[serde(rename_all = "kebab-case")]
207#[serde(deny_unknown_fields)]
208pub struct GrafanaConfig {
209    #[serde(rename = "use")]
210    phantom_use: Option<String>,
211    pub id: String,
212    pub address: String,
213    pub listen_address: String,
214    pub port: u16,
215
216    pub provide_prometheus: Option<Vec<PrometheusConfig>>,
217    pub provide_tempo: Option<Vec<TempoConfig>>,
218}
219
220#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
221#[serde(rename_all = "kebab-case")]
222#[serde(deny_unknown_fields)]
223pub struct TempoConfig {
224    #[serde(rename = "use")]
225    phantom_use: Option<String>,
226    pub id: String,
227
228    pub listen_address: String,
229    pub address: String,
230    pub port: u16,
231    pub otlp_port: u16,
232    pub max_bytes_per_trace: usize,
233}
234
235#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
236#[serde(rename_all = "kebab-case")]
237#[serde(deny_unknown_fields)]
238pub struct AwsS3Config {
239    #[serde(rename = "use")]
240    phantom_use: Option<String>,
241    pub id: String,
242    pub bucket: String,
243}
244
245#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
246#[serde(rename_all = "kebab-case")]
247#[serde(deny_unknown_fields)]
248pub struct MotoConfig {
249    #[serde(rename = "use")]
250    phantom_use: Option<String>,
251    pub id: String,
252
253    pub address: String,
254    #[serde(with = "string")]
255    pub port: u16,
256
257    pub image: String,
258    pub user_managed: bool,
259
260    pub provide_minio: Option<Vec<MinioConfig>>,
261}
262
263#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
264#[serde(rename_all = "kebab-case")]
265#[serde(deny_unknown_fields)]
266pub struct OpendalConfig {
267    #[serde(rename = "use")]
268    phantom_use: Option<String>,
269
270    pub id: String,
271    pub engine: String,
272    pub namenode: String,
273    pub bucket: String,
274}
275
276#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
277#[serde(rename_all = "kebab-case")]
278#[serde(deny_unknown_fields)]
279pub struct KafkaConfig {
280    #[serde(rename = "use")]
281    phantom_use: Option<String>,
282    pub id: String,
283
284    /// Advertise address
285    pub address: String,
286    #[serde(with = "string")]
287    pub port: u16,
288    /// Port for other services in docker. They need to connect to `host.docker.internal`, while the host
289    /// need to connect to `localhost`.
290    pub docker_port: u16,
291
292    #[serde(with = "string")]
293    pub controller_port: u16,
294
295    pub image: String,
296    pub persist_data: bool,
297    pub node_id: u32,
298
299    pub user_managed: bool,
300}
301
302#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
303#[serde(rename_all = "kebab-case")]
304#[serde(deny_unknown_fields)]
305pub struct SchemaRegistryConfig {
306    #[serde(rename = "use")]
307    phantom_use: Option<String>,
308
309    pub id: String,
310
311    pub address: String,
312    #[serde(with = "string")]
313    pub port: u16,
314
315    pub provide_kafka: Option<Vec<KafkaConfig>>,
316
317    pub image: String,
318    /// Redpanda supports schema registry natively. You can configure a `user_managed` schema registry
319    /// to use with redpanda.
320    pub user_managed: bool,
321}
322
323#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
324#[serde(rename_all = "kebab-case")]
325#[serde(deny_unknown_fields)]
326pub struct PubsubConfig {
327    #[serde(rename = "use")]
328    phantom_use: Option<String>,
329    pub id: String,
330    #[serde(default)]
331    pub user_managed: bool,
332    pub image: String,
333    #[serde(with = "string")]
334    pub port: u16,
335    pub address: String,
336
337    // `--data-dir` only preserves runtime config such as `env.yaml`; it does not
338    // persist topics, subscriptions, or messages across emulator restarts.
339    // https://github.com/risingwavelabs/risingwave/pull/26697#discussion_r3957685142
340    pub persist_data: bool,
341}
342
343#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
344#[serde(rename_all = "kebab-case")]
345#[serde(deny_unknown_fields)]
346pub struct PulsarConfig {
347    #[serde(rename = "use")]
348    phantom_use: Option<String>,
349    pub id: String,
350
351    pub address: String,
352    pub broker_port: u16,
353    pub http_port: u16,
354
355    pub user_managed: bool,
356    pub image: String,
357    pub persist_data: bool,
358}
359
360#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
361#[serde(rename_all = "kebab-case")]
362#[serde(deny_unknown_fields)]
363pub struct RedisConfig {
364    #[serde(rename = "use")]
365    phantom_use: Option<String>,
366    pub id: String,
367
368    pub port: u16,
369    pub address: String,
370
371    pub image: String,
372    pub user_managed: bool,
373    pub persist_data: bool,
374}
375
376#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
377#[serde(rename_all = "kebab-case")]
378#[serde(deny_unknown_fields)]
379pub struct ClickHouseConfig {
380    #[serde(rename = "use")]
381    phantom_use: Option<String>,
382    pub id: String,
383
384    pub address: String,
385    pub http_port: u16,
386    pub native_port: u16,
387
388    pub user: String,
389    pub password: String,
390    pub database: String,
391
392    pub image: String,
393    pub user_managed: bool,
394    pub persist_data: bool,
395}
396
397#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
398#[serde(rename_all = "kebab-case")]
399#[serde(deny_unknown_fields)]
400pub enum Application {
401    Metastore,
402    Connector,
403}
404
405#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
406#[serde(rename_all = "kebab-case")]
407#[serde(deny_unknown_fields)]
408pub struct MySqlConfig {
409    #[serde(rename = "use")]
410    phantom_use: Option<String>,
411    pub id: String,
412
413    pub port: u16,
414    pub address: String,
415
416    pub user: String,
417    pub password: String,
418    pub database: String,
419
420    pub application: Application,
421    pub image: String,
422    pub user_managed: bool,
423    pub persist_data: bool,
424}
425
426#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
427#[serde(rename_all = "kebab-case")]
428#[serde(deny_unknown_fields)]
429pub struct PostgresConfig {
430    #[serde(rename = "use")]
431    phantom_use: Option<String>,
432    pub id: String,
433
434    pub port: u16,
435    pub address: String,
436
437    pub user: String,
438    pub password: String,
439    pub database: String,
440
441    pub application: Application,
442    pub image: String,
443    pub user_managed: bool,
444    pub persist_data: bool,
445
446    // Inject latency into any network calls to the postgres service.
447    pub latency_ms: u32,
448}
449
450#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
451#[serde(rename_all = "kebab-case")]
452#[serde(deny_unknown_fields)]
453pub struct OracleConfig {
454    #[serde(rename = "use")]
455    phantom_use: Option<String>,
456    pub id: String,
457
458    pub port: u16,
459    pub address: String,
460
461    pub user: String,
462    pub password: String,
463    pub database: String,
464    pub pdb: String,
465
466    pub image: String,
467    pub user_managed: bool,
468    pub persist_data: bool,
469}
470
471#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
472#[serde(rename_all = "kebab-case")]
473#[serde(deny_unknown_fields)]
474pub struct SqlServerConfig {
475    #[serde(rename = "use")]
476    phantom_use: Option<String>,
477    pub id: String,
478
479    pub port: u16,
480    pub address: String,
481
482    pub user: String,
483    pub password: String,
484    pub database: String,
485
486    pub image: String,
487    pub user_managed: bool,
488    pub persist_data: bool,
489}
490
491#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
492#[serde(rename_all = "kebab-case")]
493#[serde(deny_unknown_fields)]
494pub struct MongoDbConfig {
495    #[serde(rename = "use")]
496    phantom_use: Option<String>,
497    pub id: String,
498
499    pub address: String,
500    pub port: u16,
501
502    pub image: String,
503    pub user_managed: bool,
504    pub persist_data: bool,
505}
506
507#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
508#[serde(rename_all = "kebab-case")]
509#[serde(deny_unknown_fields)]
510pub struct NatsConfig {
511    #[serde(rename = "use")]
512    phantom_use: Option<String>,
513    pub id: String,
514
515    pub address: String,
516    pub port: u16,
517    pub monitor_port: u16,
518
519    pub image: String,
520    pub user_managed: bool,
521    pub persist_data: bool,
522}
523
524#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
525#[serde(rename_all = "kebab-case")]
526#[serde(deny_unknown_fields)]
527pub struct MqttConfig {
528    #[serde(rename = "use")]
529    phantom_use: Option<String>,
530    pub id: String,
531
532    pub address: String,
533    pub port: u16,
534
535    pub image: String,
536    pub user_managed: bool,
537    pub persist_data: bool,
538}
539
540#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
541#[serde(rename_all = "kebab-case")]
542#[serde(deny_unknown_fields)]
543pub struct ElasticSearchConfig {
544    #[serde(rename = "use")]
545    phantom_use: Option<String>,
546    pub id: String,
547
548    pub address: String,
549    pub port: u16,
550
551    pub user: String,
552    pub password: String,
553
554    pub image: String,
555    pub user_managed: bool,
556    pub persist_data: bool,
557}
558
559#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
560#[serde(rename_all = "kebab-case")]
561#[serde(deny_unknown_fields)]
562pub struct OpenSearchConfig {
563    #[serde(rename = "use")]
564    phantom_use: Option<String>,
565    pub id: String,
566
567    pub address: String,
568    pub port: u16,
569
570    pub user: String,
571    pub password: String,
572
573    pub image: String,
574    pub user_managed: bool,
575    pub persist_data: bool,
576}
577
578#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
579#[serde(rename_all = "kebab-case")]
580#[serde(deny_unknown_fields)]
581pub struct LakekeeperConfig {
582    #[serde(rename = "use")]
583    phantom_use: Option<String>,
584    pub id: String,
585
586    pub port: u16,
587    pub address: String,
588
589    pub user_managed: bool,
590    pub persist_data: bool,
591
592    pub catalog_backend: String,
593    pub encryption_key: String,
594    pub provide_postgres_backend: Option<Vec<PostgresConfig>>,
595    pub provide_minio: Option<Vec<MinioConfig>>,
596}
597
598#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
599#[serde(rename_all = "kebab-case")]
600#[serde(deny_unknown_fields)]
601pub struct MoatConfig {
602    #[serde(rename = "use")]
603    phantom_use: Option<String>,
604    pub id: String,
605
606    pub address: String,
607    pub port: u16,
608
609    pub provide_minio: Option<Vec<MinioConfig>>,
610}
611
612/// All service configuration
613#[derive(Clone, Debug, PartialEq)]
614pub enum ServiceConfig {
615    ComputeNode(ComputeNodeConfig),
616    MetaNode(MetaNodeConfig),
617    Frontend(FrontendConfig),
618    Compactor(CompactorConfig),
619    Minio(MinioConfig),
620    Sqlite(SqliteConfig),
621    Prometheus(PrometheusConfig),
622    Grafana(GrafanaConfig),
623    Tempo(TempoConfig),
624    Opendal(OpendalConfig),
625    AwsS3(AwsS3Config),
626    Moto(MotoConfig),
627    Kafka(KafkaConfig),
628    SchemaRegistry(SchemaRegistryConfig),
629    Pubsub(PubsubConfig),
630    Pulsar(PulsarConfig),
631    Redis(RedisConfig),
632    ClickHouse(ClickHouseConfig),
633    MySql(MySqlConfig),
634    Postgres(PostgresConfig),
635    Oracle(OracleConfig),
636    SqlServer(SqlServerConfig),
637    MongoDb(MongoDbConfig),
638    ElasticSearch(ElasticSearchConfig),
639    OpenSearch(OpenSearchConfig),
640    Nats(NatsConfig),
641    Mqtt(MqttConfig),
642    Lakekeeper(LakekeeperConfig),
643    Moat(MoatConfig),
644}
645
646#[derive(PartialEq, Eq, Hash, Debug)]
647pub enum TaskGroup {
648    RisingWave,
649    Observability,
650    Kafka,
651    Pubsub,
652    Pulsar,
653    MySql,
654    Postgres,
655    Oracle,
656    SqlServer,
657    MongoDb,
658    ElasticSearch,
659    OpenSearch,
660    Nats,
661    Mqtt,
662    Redis,
663    ClickHouse,
664    Lakekeeper,
665    Moat,
666}
667
668impl ServiceConfig {
669    pub fn id(&self) -> &str {
670        match self {
671            Self::ComputeNode(c) => &c.id,
672            Self::MetaNode(c) => &c.id,
673            Self::Frontend(c) => &c.id,
674            Self::Compactor(c) => &c.id,
675            Self::Minio(c) => &c.id,
676            Self::Sqlite(c) => &c.id,
677            Self::Prometheus(c) => &c.id,
678            Self::Grafana(c) => &c.id,
679            Self::Tempo(c) => &c.id,
680            Self::AwsS3(c) => &c.id,
681            Self::Moto(c) => &c.id,
682            Self::Kafka(c) => &c.id,
683            Self::Pubsub(c) => &c.id,
684            Self::Pulsar(c) => &c.id,
685            Self::Redis(c) => &c.id,
686            Self::ClickHouse(c) => &c.id,
687            Self::Opendal(c) => &c.id,
688            Self::MySql(c) => &c.id,
689            Self::Postgres(c) => &c.id,
690            Self::Oracle(c) => &c.id,
691            Self::SqlServer(c) => &c.id,
692            Self::MongoDb(c) => &c.id,
693            Self::ElasticSearch(c) => &c.id,
694            Self::OpenSearch(c) => &c.id,
695            Self::Nats(c) => &c.id,
696            Self::Mqtt(c) => &c.id,
697            Self::SchemaRegistry(c) => &c.id,
698            Self::Lakekeeper(c) => &c.id,
699            Self::Moat(c) => &c.id,
700        }
701    }
702
703    /// Used to check whether the port is occupied before running the service.
704    pub fn port(&self) -> Option<u16> {
705        match self {
706            Self::ComputeNode(c) => Some(c.port),
707            Self::MetaNode(c) => Some(c.port),
708            Self::Frontend(c) => Some(c.port),
709            Self::Compactor(c) => Some(c.port),
710            Self::Minio(c) => Some(c.port),
711            Self::Sqlite(_) => None,
712            Self::Prometheus(c) => Some(c.port),
713            Self::Grafana(c) => Some(c.port),
714            Self::Tempo(c) => Some(c.port),
715            Self::AwsS3(_) => None,
716            Self::Moto(c) => Some(c.port),
717            Self::Kafka(c) => Some(c.port),
718            Self::Pubsub(c) => Some(c.port),
719            Self::Pulsar(c) => Some(c.http_port),
720            Self::Redis(c) => Some(c.port),
721            Self::ClickHouse(c) => Some(c.http_port),
722            Self::Opendal(_) => None,
723            Self::MySql(c) => Some(c.port),
724            Self::Postgres(c) => Some(c.port),
725            Self::Oracle(c) => Some(c.port),
726            Self::SqlServer(c) => Some(c.port),
727            Self::MongoDb(c) => Some(c.port),
728            Self::ElasticSearch(c) => Some(c.port),
729            Self::OpenSearch(c) => Some(c.port),
730            Self::Nats(c) => Some(c.port),
731            Self::Mqtt(c) => Some(c.port),
732            Self::SchemaRegistry(c) => Some(c.port),
733            Self::Lakekeeper(c) => Some(c.port),
734            Self::Moat(c) => Some(c.port),
735        }
736    }
737
738    pub fn user_managed(&self) -> bool {
739        match self {
740            Self::ComputeNode(c) => c.user_managed,
741            Self::MetaNode(c) => c.user_managed,
742            Self::Frontend(c) => c.user_managed,
743            Self::Compactor(c) => c.user_managed,
744            Self::Minio(_c) => false,
745            Self::Sqlite(_c) => false,
746            Self::Prometheus(_c) => false,
747            Self::Grafana(_c) => false,
748            Self::Tempo(_c) => false,
749            Self::AwsS3(_c) => false,
750            Self::Moto(c) => c.user_managed,
751            Self::Kafka(c) => c.user_managed,
752            Self::Pubsub(c) => c.user_managed,
753            Self::Pulsar(c) => c.user_managed,
754            Self::Redis(c) => c.user_managed,
755            Self::ClickHouse(c) => c.user_managed,
756            Self::Opendal(_c) => false,
757            Self::MySql(c) => c.user_managed,
758            Self::Postgres(c) => c.user_managed,
759            Self::Oracle(c) => c.user_managed,
760            Self::SqlServer(c) => c.user_managed,
761            Self::MongoDb(c) => c.user_managed,
762            Self::ElasticSearch(c) => c.user_managed,
763            Self::OpenSearch(c) => c.user_managed,
764            Self::Nats(c) => c.user_managed,
765            Self::Mqtt(c) => c.user_managed,
766            Self::SchemaRegistry(c) => c.user_managed,
767            Self::Lakekeeper(c) => c.user_managed,
768            Self::Moat(_c) => false,
769        }
770    }
771
772    pub fn task_group(&self) -> TaskGroup {
773        use TaskGroup::*;
774        match self {
775            ServiceConfig::ComputeNode(_)
776            | ServiceConfig::MetaNode(_)
777            | ServiceConfig::Frontend(_)
778            | ServiceConfig::Compactor(_)
779            | ServiceConfig::Minio(_)
780            | ServiceConfig::Sqlite(_) => RisingWave,
781            ServiceConfig::Prometheus(_) | ServiceConfig::Grafana(_) | ServiceConfig::Tempo(_) => {
782                Observability
783            }
784            ServiceConfig::Opendal(_) | ServiceConfig::AwsS3(_) => RisingWave,
785            ServiceConfig::Moto(_) => RisingWave,
786            ServiceConfig::Kafka(_) | ServiceConfig::SchemaRegistry(_) => Kafka,
787            ServiceConfig::Pubsub(_) => Pubsub,
788            ServiceConfig::Pulsar(_) => Pulsar,
789            ServiceConfig::Redis(_) => Redis,
790            ServiceConfig::ClickHouse(_) => ClickHouse,
791            ServiceConfig::MySql(my_sql_config) => {
792                if matches!(my_sql_config.application, Application::Metastore) {
793                    RisingWave
794                } else {
795                    MySql
796                }
797            }
798            ServiceConfig::Postgres(postgres_config) => {
799                if matches!(postgres_config.application, Application::Metastore) {
800                    RisingWave
801                } else {
802                    Postgres
803                }
804            }
805            ServiceConfig::Oracle(_) => Oracle,
806            ServiceConfig::SqlServer(_) => SqlServer,
807            ServiceConfig::MongoDb(_) => MongoDb,
808            ServiceConfig::ElasticSearch(_) => ElasticSearch,
809            ServiceConfig::OpenSearch(_) => OpenSearch,
810            ServiceConfig::Nats(_) => Nats,
811            ServiceConfig::Mqtt(_) => Mqtt,
812            ServiceConfig::Lakekeeper(_) => Lakekeeper,
813            ServiceConfig::Moat(_) => Moat,
814        }
815    }
816}
817
818mod string {
819    use std::fmt::Display;
820    use std::str::FromStr;
821
822    use serde::{Deserialize, Deserializer, Serializer, de};
823
824    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
825    where
826        T: Display,
827        S: Serializer,
828    {
829        serializer.collect_str(value)
830    }
831
832    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
833    where
834        T: FromStr,
835        T::Err: Display,
836        D: Deserializer<'de>,
837    {
838        String::deserialize(deserializer)?
839            .parse()
840            .map_err(de::Error::custom)
841    }
842}