Skip to main content

sqlness_runner/
server_mode.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 std::collections::{HashMap, HashSet};
16use std::path::Path;
17use std::sync::{Mutex, OnceLock};
18
19use serde::Serialize;
20use tinytemplate::TinyTemplate;
21
22use crate::cmd::bare::ServerAddr;
23use crate::cmd::compat_case::Version;
24use crate::env::bare::{Env, GreptimeDBContext, ServiceProvider};
25use crate::util;
26
27const DEFAULT_LOG_LEVEL: &str = "--log-level=debug,hyper=warn,tower=warn,datafusion=warn,reqwest=warn,sqlparser=warn,h2=info,opendal=info";
28
29/// Which set of gRPC CLI argument names to use when spawning a GreptimeDB binary.
30///
31/// The CLI rename from `rpc-*` to `grpc-*` landed before v1.1.0.  Older release
32/// binaries (e.g. v1.0.0) only recognize `--rpc-bind-addr` and
33/// `--rpc-server-addr`; v1.1.0+ and current binaries use `--grpc-*` names while
34/// keeping the old names as hidden aliases.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum GrpcArgStyle {
37    /// Current-style: `--grpc-bind-addr` / `--grpc-server-addr`
38    Grpc,
39    /// Legacy-style: `--rpc-bind-addr` / `--rpc-server-addr`
40    Rpc,
41}
42
43impl GrpcArgStyle {
44    /// Chooses the argument style from an inferred GreptimeDB binary version.
45    ///
46    /// Unknown versions are treated as current/development binaries and use the
47    /// official `grpc-*` names.
48    pub(crate) fn for_version(version: Option<&Version>) -> Self {
49        const GRPC_ARG_RENAME_VERSION: Version = Version {
50            major: 1,
51            minor: 1,
52            patch: 0,
53        };
54
55        if version.is_some_and(|version| version < &GRPC_ARG_RENAME_VERSION) {
56            GrpcArgStyle::Rpc
57        } else {
58            GrpcArgStyle::Grpc
59        }
60    }
61
62    /// Returns the CLI flag name for the gRPC bind address.
63    pub fn bind_addr_arg(self) -> &'static str {
64        match self {
65            GrpcArgStyle::Grpc => "--grpc-bind-addr",
66            GrpcArgStyle::Rpc => "--rpc-bind-addr",
67        }
68    }
69
70    /// Returns the CLI flag name for the gRPC server (advertised) address.
71    pub fn server_addr_arg(self) -> &'static str {
72        match self {
73            GrpcArgStyle::Grpc => "--grpc-server-addr",
74            GrpcArgStyle::Rpc => "--rpc-server-addr",
75        }
76    }
77}
78
79static USED_PORTS: OnceLock<Mutex<HashSet<u16>>> = OnceLock::new();
80
81fn get_used_ports() -> &'static Mutex<HashSet<u16>> {
82    USED_PORTS.get_or_init(|| Mutex::new(HashSet::new()))
83}
84
85fn get_unique_random_port() -> u16 {
86    // Tricky loop 100 times to find an unused port instead of infinite loop.
87    const MAX_ATTEMPTS: usize = 100;
88
89    for _ in 0..MAX_ATTEMPTS {
90        let p = util::get_random_port();
91        let mut used = get_used_ports().lock().unwrap();
92        if !used.contains(&p) {
93            used.insert(p);
94            return p;
95        }
96    }
97
98    panic!(
99        "Failed to find an unused port after {} attempts",
100        MAX_ATTEMPTS
101    );
102}
103
104#[derive(Clone)]
105pub enum ServerMode {
106    Standalone {
107        http_addr: String,
108        rpc_bind_addr: String,
109        mysql_addr: String,
110        postgres_addr: String,
111    },
112    Frontend {
113        http_addr: String,
114        rpc_bind_addr: String,
115        mysql_addr: String,
116        postgres_addr: String,
117        metasrv_addr: String,
118    },
119    Metasrv {
120        rpc_bind_addr: String,
121        rpc_server_addr: String,
122        http_addr: String,
123    },
124    Datanode {
125        rpc_bind_addr: String,
126        rpc_server_addr: String,
127        http_addr: String,
128        metasrv_addr: String,
129        node_id: u32,
130    },
131    Flownode {
132        rpc_bind_addr: String,
133        rpc_server_addr: String,
134        http_addr: String,
135        metasrv_addr: String,
136        node_id: u32,
137    },
138}
139
140#[derive(Serialize)]
141struct ConfigContext {
142    wal_dir: String,
143    data_home: String,
144    procedure_dir: String,
145    is_raft_engine: bool,
146    kafka_wal_broker_endpoints: String,
147    use_etcd: bool,
148    store_addrs: String,
149    instance_id: usize,
150    addrs: HashMap<String, String>,
151    // enable flat format for storage engine
152    enable_flat_format: bool,
153    // enable garbage collection in metasrv and datanodes
154    enable_gc: bool,
155}
156
157impl ServerMode {
158    pub fn random_standalone() -> Self {
159        let http_port = get_unique_random_port();
160        let rpc_port = get_unique_random_port();
161        let mysql_port = get_unique_random_port();
162        let postgres_port = get_unique_random_port();
163
164        ServerMode::Standalone {
165            http_addr: format!("127.0.0.1:{http_port}"),
166            rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
167            mysql_addr: format!("127.0.0.1:{mysql_port}"),
168            postgres_addr: format!("127.0.0.1:{postgres_port}"),
169        }
170    }
171
172    pub fn random_frontend(metasrv_port: u16) -> Self {
173        let http_port = get_unique_random_port();
174        let rpc_port = get_unique_random_port();
175        let mysql_port = get_unique_random_port();
176        let postgres_port = get_unique_random_port();
177
178        ServerMode::Frontend {
179            http_addr: format!("127.0.0.1:{http_port}"),
180            rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
181            mysql_addr: format!("127.0.0.1:{mysql_port}"),
182            postgres_addr: format!("127.0.0.1:{postgres_port}"),
183            metasrv_addr: format!("127.0.0.1:{metasrv_port}"),
184        }
185    }
186
187    pub fn random_metasrv() -> Self {
188        let bind_port = get_unique_random_port();
189        let http_port = get_unique_random_port();
190
191        ServerMode::Metasrv {
192            rpc_bind_addr: format!("127.0.0.1:{bind_port}"),
193            rpc_server_addr: format!("127.0.0.1:{bind_port}"),
194            http_addr: format!("127.0.0.1:{http_port}"),
195        }
196    }
197
198    pub fn random_datanode(metasrv_port: u16, node_id: u32) -> Self {
199        let rpc_port = get_unique_random_port();
200        let http_port = get_unique_random_port();
201
202        ServerMode::Datanode {
203            rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
204            rpc_server_addr: format!("127.0.0.1:{rpc_port}"),
205            http_addr: format!("127.0.0.1:{http_port}"),
206            metasrv_addr: format!("127.0.0.1:{metasrv_port}"),
207            node_id,
208        }
209    }
210
211    pub fn random_flownode(metasrv_port: u16, node_id: u32) -> Self {
212        let rpc_port = get_unique_random_port();
213        let http_port = get_unique_random_port();
214
215        ServerMode::Flownode {
216            rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
217            rpc_server_addr: format!("127.0.0.1:{rpc_port}"),
218            http_addr: format!("127.0.0.1:{http_port}"),
219            metasrv_addr: format!("127.0.0.1:{metasrv_port}"),
220            node_id,
221        }
222    }
223
224    pub fn name(&self) -> &'static str {
225        match self {
226            ServerMode::Standalone { .. } => "standalone",
227            ServerMode::Frontend { .. } => "frontend",
228            ServerMode::Metasrv { .. } => "metasrv",
229            ServerMode::Datanode { .. } => "datanode",
230            ServerMode::Flownode { .. } => "flownode",
231        }
232    }
233
234    /// Returns the addresses of the server that needed to be checked.
235    pub fn check_addrs(&self) -> Vec<String> {
236        match self {
237            ServerMode::Standalone {
238                rpc_bind_addr,
239                mysql_addr,
240                postgres_addr,
241                http_addr,
242                ..
243            } => {
244                vec![
245                    rpc_bind_addr.clone(),
246                    mysql_addr.clone(),
247                    postgres_addr.clone(),
248                    http_addr.clone(),
249                ]
250            }
251            ServerMode::Frontend {
252                rpc_bind_addr,
253                mysql_addr,
254                postgres_addr,
255                ..
256            } => {
257                vec![
258                    rpc_bind_addr.clone(),
259                    mysql_addr.clone(),
260                    postgres_addr.clone(),
261                ]
262            }
263            ServerMode::Metasrv { rpc_bind_addr, .. } => {
264                vec![rpc_bind_addr.clone()]
265            }
266            ServerMode::Datanode { rpc_bind_addr, .. } => {
267                vec![rpc_bind_addr.clone()]
268            }
269            ServerMode::Flownode { rpc_bind_addr, .. } => {
270                vec![rpc_bind_addr.clone()]
271            }
272        }
273    }
274
275    /// Returns the server addresses to connect. Only standalone and frontend mode have this.
276    pub fn server_addr(&self) -> Option<ServerAddr> {
277        match self {
278            ServerMode::Standalone {
279                rpc_bind_addr,
280                mysql_addr,
281                postgres_addr,
282                ..
283            } => Some(ServerAddr {
284                server_addr: Some(rpc_bind_addr.clone()),
285                pg_server_addr: Some(postgres_addr.clone()),
286                mysql_server_addr: Some(mysql_addr.clone()),
287            }),
288            ServerMode::Frontend {
289                rpc_bind_addr,
290                mysql_addr,
291                postgres_addr,
292                ..
293            } => Some(ServerAddr {
294                server_addr: Some(rpc_bind_addr.clone()),
295                pg_server_addr: Some(postgres_addr.clone()),
296                mysql_server_addr: Some(mysql_addr.clone()),
297            }),
298            _ => None,
299        }
300    }
301
302    pub fn generate_config_file(
303        &self,
304        sqlness_home: &Path,
305        db_ctx: &GreptimeDBContext,
306        id: usize,
307    ) -> String {
308        let mut tt = TinyTemplate::new();
309
310        let mut path = util::sqlness_conf_path();
311        path.push(format!("{}-test.toml.template", self.name()));
312        let template = std::fs::read_to_string(&path)
313            .unwrap_or_else(|e| panic!("read file '{}' error: {e}", path.display()));
314        tt.add_template(self.name(), &template).unwrap();
315
316        let data_home = sqlness_home.join(format!("greptimedb-{}-{}", id, self.name()));
317        std::fs::create_dir_all(data_home.as_path()).unwrap();
318
319        let wal_dir = data_home.join("wal").display().to_string();
320        let procedure_dir = data_home.join("procedure").display().to_string();
321
322        // Get the required addresses based on server mode
323        let addrs: HashMap<String, String> = match self {
324            ServerMode::Standalone {
325                rpc_bind_addr,
326                mysql_addr,
327                postgres_addr,
328                http_addr,
329            } => [
330                ("http_addr".to_string(), http_addr.clone()),
331                ("grpc_addr".to_string(), rpc_bind_addr.clone()),
332                ("mysql_addr".to_string(), mysql_addr.clone()),
333                ("postgres_addr".to_string(), postgres_addr.clone()),
334            ]
335            .into(),
336            ServerMode::Frontend { rpc_bind_addr, .. } => {
337                [("grpc_addr".to_string(), rpc_bind_addr.clone())].into()
338            }
339            ServerMode::Datanode { metasrv_addr, .. } => {
340                [("metasrv_addr".to_string(), metasrv_addr.clone())].into()
341            }
342            _ => HashMap::new(),
343        };
344
345        let ctx = ConfigContext {
346            wal_dir,
347            data_home: data_home.display().to_string(),
348            procedure_dir,
349            is_raft_engine: db_ctx.is_raft_engine(),
350            kafka_wal_broker_endpoints: db_ctx.kafka_wal_broker_endpoints(),
351            use_etcd: !db_ctx.store_config().store_addrs.is_empty(),
352            store_addrs: db_ctx
353                .store_config()
354                .store_addrs
355                .iter()
356                .map(|p| format!("\"{p}\""))
357                .collect::<Vec<_>>()
358                .join(","),
359            instance_id: id,
360            addrs,
361            enable_flat_format: db_ctx.store_config().enable_flat_format,
362            enable_gc: db_ctx.store_config().enable_gc,
363        };
364
365        let rendered = tt.render(self.name(), &ctx).unwrap();
366
367        let conf_file = data_home
368            .join(format!("{}-{}-{}.toml", self.name(), id, db_ctx.time()))
369            .display()
370            .to_string();
371        println!(
372            "Generating id {}, {} config file in {conf_file}",
373            id,
374            self.name()
375        );
376        std::fs::write(&conf_file, rendered).unwrap();
377
378        conf_file
379    }
380
381    pub fn get_args(
382        &self,
383        sqlness_home: &Path,
384        env: &Env,
385        db_ctx: &GreptimeDBContext,
386        id: usize,
387        arg_style: GrpcArgStyle,
388    ) -> Vec<String> {
389        let mut args = env
390            .extra_args()
391            .iter()
392            .map(String::as_str)
393            .chain([DEFAULT_LOG_LEVEL, self.name(), "start"])
394            .map(ToString::to_string)
395            .collect::<Vec<String>>();
396
397        match self {
398            ServerMode::Standalone {
399                http_addr,
400                rpc_bind_addr,
401                mysql_addr,
402                postgres_addr,
403            } => {
404                args.extend([
405                    format!(
406                        "--log-dir={}/greptimedb-{}-standalone/logs",
407                        sqlness_home.display(),
408                        id
409                    ),
410                    "-c".to_string(),
411                    self.generate_config_file(sqlness_home, db_ctx, id),
412                    format!("--http-addr={http_addr}"),
413                    format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
414                    format!("--mysql-addr={mysql_addr}"),
415                    format!("--postgres-addr={postgres_addr}"),
416                ]);
417            }
418            ServerMode::Frontend {
419                http_addr,
420                rpc_bind_addr,
421                mysql_addr,
422                postgres_addr,
423                metasrv_addr,
424            } => {
425                args.extend([
426                    format!("--metasrv-addrs={metasrv_addr}"),
427                    format!("--http-addr={http_addr}"),
428                    format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
429                    // since sqlness run on local, bind addr is the same as server addr
430                    // this is needed so that `cluster_info`'s server addr column can be correct
431                    format!("{}={rpc_bind_addr}", arg_style.server_addr_arg()),
432                    format!("--mysql-addr={mysql_addr}"),
433                    format!("--postgres-addr={postgres_addr}"),
434                    format!(
435                        "--log-dir={}/greptimedb-{}-frontend/logs",
436                        sqlness_home.display(),
437                        id
438                    ),
439                    "-c".to_string(),
440                    self.generate_config_file(sqlness_home, db_ctx, id),
441                ]);
442            }
443            ServerMode::Metasrv {
444                rpc_bind_addr,
445                rpc_server_addr,
446                http_addr,
447            } => {
448                args.extend([
449                    arg_style.bind_addr_arg().to_string(),
450                    rpc_bind_addr.clone(),
451                    arg_style.server_addr_arg().to_string(),
452                    rpc_server_addr.clone(),
453                    "--enable-region-failover".to_string(),
454                    "false".to_string(),
455                    format!("--http-addr={http_addr}"),
456                    format!(
457                        "--log-dir={}/greptimedb-{}-metasrv/logs",
458                        sqlness_home.display(),
459                        id
460                    ),
461                    "-c".to_string(),
462                    self.generate_config_file(sqlness_home, db_ctx, id),
463                ]);
464
465                if matches!(
466                    db_ctx.store_config().setup_pg,
467                    Some(ServiceProvider::Create)
468                ) {
469                    let client_ports = db_ctx
470                        .store_config()
471                        .store_addrs
472                        .iter()
473                        .map(|s| s.split(':').nth(1).unwrap().parse::<u16>().unwrap())
474                        .collect::<Vec<_>>();
475                    let client_port = client_ports.first().unwrap_or(&5432);
476                    let pg_server_addr = format!(
477                        "postgresql://greptimedb:admin@127.0.0.1:{}/postgres",
478                        client_port
479                    );
480                    args.extend(vec!["--backend".to_string(), "postgres-store".to_string()]);
481                    args.extend(vec!["--store-addrs".to_string(), pg_server_addr]);
482                } else if let Some(ServiceProvider::External(connection_string)) =
483                    db_ctx.store_config().setup_pg
484                {
485                    println!("Using external PostgreSQL '{connection_string}' as Kvbackend");
486                    args.extend([
487                        "--backend".to_string(),
488                        "postgres-store".to_string(),
489                        "--store-addrs".to_string(),
490                        connection_string,
491                    ]);
492                } else if matches!(
493                    db_ctx.store_config().setup_mysql,
494                    Some(ServiceProvider::Create)
495                ) {
496                    let client_ports = db_ctx
497                        .store_config()
498                        .store_addrs
499                        .iter()
500                        .map(|s| s.split(':').nth(1).unwrap().parse::<u16>().unwrap())
501                        .collect::<Vec<_>>();
502                    let client_port = client_ports.first().unwrap_or(&3306);
503                    let mysql_server_addr =
504                        format!("mysql://greptimedb:admin@127.0.0.1:{}/mysql", client_port);
505                    args.extend(vec!["--backend".to_string(), "mysql-store".to_string()]);
506                    args.extend(vec!["--store-addrs".to_string(), mysql_server_addr]);
507                } else if let Some(ServiceProvider::External(connection_string)) =
508                    db_ctx.store_config().setup_mysql
509                {
510                    println!("Using external MySQL '{connection_string}' as Kvbackend");
511                    args.extend([
512                        "--backend".to_string(),
513                        "mysql-store".to_string(),
514                        "--store-addrs".to_string(),
515                        connection_string,
516                    ]);
517                } else if db_ctx.store_config().store_addrs.is_empty() {
518                    args.extend(vec!["--backend".to_string(), "memory-store".to_string()])
519                }
520            }
521            ServerMode::Datanode {
522                rpc_bind_addr,
523                rpc_server_addr,
524                http_addr,
525                metasrv_addr,
526                node_id,
527            } => {
528                let data_home = sqlness_home.join(format!(
529                    "greptimedb_{}_datanode_{}_{node_id}",
530                    id,
531                    db_ctx.time()
532                ));
533                args.extend([
534                    format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
535                    format!("{}={rpc_server_addr}", arg_style.server_addr_arg()),
536                    format!("--http-addr={http_addr}"),
537                    format!("--data-home={}", data_home.display()),
538                    format!("--log-dir={}/logs", data_home.display()),
539                    format!("--node-id={node_id}"),
540                    "-c".to_string(),
541                    self.generate_config_file(sqlness_home, db_ctx, id),
542                    format!("--metasrv-addrs={metasrv_addr}"),
543                ]);
544            }
545            ServerMode::Flownode {
546                rpc_bind_addr,
547                rpc_server_addr,
548                http_addr,
549                metasrv_addr,
550                node_id,
551            } => {
552                args.extend([
553                    format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
554                    format!("{}={rpc_server_addr}", arg_style.server_addr_arg()),
555                    format!("--node-id={node_id}"),
556                    format!(
557                        "--log-dir={}/greptimedb-{}-flownode/logs",
558                        sqlness_home.display(),
559                        id
560                    ),
561                    format!("--metasrv-addrs={metasrv_addr}"),
562                    format!("--http-addr={http_addr}"),
563                ]);
564            }
565        }
566
567        args
568    }
569}
570
571#[cfg(test)]
572mod tests {
573    use std::path::PathBuf;
574
575    use super::*;
576    use crate::env::bare::{StoreConfig, WalConfig};
577
578    fn test_env(sqlness_home: &Path) -> (Env, GreptimeDBContext) {
579        let store_config = StoreConfig {
580            store_addrs: vec!["127.0.0.1:2379".to_string()],
581            setup_etcd: true,
582            setup_pg: None,
583            setup_mysql: None,
584            enable_flat_format: false,
585            enable_gc: false,
586        };
587        let env = Env::new(
588            sqlness_home.to_path_buf(),
589            ServerAddr::default(),
590            WalConfig::RaftEngine,
591            false,
592            Some(PathBuf::from(".")),
593            store_config.clone(),
594            vec![],
595        );
596        let db_ctx = GreptimeDBContext::new(WalConfig::RaftEngine, store_config);
597
598        (env, db_ctx)
599    }
600
601    fn has_arg(args: &[String], name: &str) -> bool {
602        let prefix = format!("{name}=");
603        args.iter()
604            .any(|arg| arg == name || arg.starts_with(&prefix))
605    }
606
607    fn assert_uses_style(args: &[String], style: GrpcArgStyle, expect_server_addr: bool) {
608        let bind = style.bind_addr_arg();
609        let server = style.server_addr_arg();
610        let other_bind = match style {
611            GrpcArgStyle::Grpc => "--rpc-bind-addr",
612            GrpcArgStyle::Rpc => "--grpc-bind-addr",
613        };
614        let other_server = match style {
615            GrpcArgStyle::Grpc => "--rpc-server-addr",
616            GrpcArgStyle::Rpc => "--grpc-server-addr",
617        };
618
619        assert!(has_arg(args, bind), "missing {bind} in args: {args:?}");
620        assert!(
621            !args.iter().any(|arg| arg.contains(other_bind)),
622            "unexpected {other_bind} in args: {args:?}"
623        );
624
625        if expect_server_addr {
626            assert!(has_arg(args, server), "missing {server} in args: {args:?}");
627            assert!(
628                !args.iter().any(|arg| arg.contains(other_server)),
629                "unexpected {other_server} in args: {args:?}"
630            );
631        }
632    }
633
634    fn test_all_modes(env: &Env, db_ctx: &GreptimeDBContext, temp_dir: &Path, style: GrpcArgStyle) {
635        let standalone = ServerMode::Standalone {
636            http_addr: "127.0.0.1:4000".to_string(),
637            rpc_bind_addr: "127.0.0.1:4001".to_string(),
638            mysql_addr: "127.0.0.1:4002".to_string(),
639            postgres_addr: "127.0.0.1:4003".to_string(),
640        };
641        assert_uses_style(
642            &standalone.get_args(temp_dir, env, db_ctx, 0, style),
643            style,
644            false,
645        );
646
647        let frontend = ServerMode::Frontend {
648            http_addr: "127.0.0.1:4100".to_string(),
649            rpc_bind_addr: "127.0.0.1:4101".to_string(),
650            mysql_addr: "127.0.0.1:4102".to_string(),
651            postgres_addr: "127.0.0.1:4103".to_string(),
652            metasrv_addr: "127.0.0.1:4001".to_string(),
653        };
654        assert_uses_style(
655            &frontend.get_args(temp_dir, env, db_ctx, 0, style),
656            style,
657            true,
658        );
659
660        let metasrv = ServerMode::Metasrv {
661            rpc_bind_addr: "127.0.0.1:4201".to_string(),
662            rpc_server_addr: "127.0.0.1:4201".to_string(),
663            http_addr: "127.0.0.1:4200".to_string(),
664        };
665        assert_uses_style(
666            &metasrv.get_args(temp_dir, env, db_ctx, 0, style),
667            style,
668            true,
669        );
670
671        let datanode = ServerMode::Datanode {
672            rpc_bind_addr: "127.0.0.1:4301".to_string(),
673            rpc_server_addr: "127.0.0.1:4301".to_string(),
674            http_addr: "127.0.0.1:4300".to_string(),
675            metasrv_addr: "127.0.0.1:4001".to_string(),
676            node_id: 0,
677        };
678        assert_uses_style(
679            &datanode.get_args(temp_dir, env, db_ctx, 0, style),
680            style,
681            true,
682        );
683
684        let flownode = ServerMode::Flownode {
685            rpc_bind_addr: "127.0.0.1:4401".to_string(),
686            rpc_server_addr: "127.0.0.1:4401".to_string(),
687            http_addr: "127.0.0.1:4400".to_string(),
688            metasrv_addr: "127.0.0.1:4001".to_string(),
689            node_id: 0,
690        };
691        assert_uses_style(
692            &flownode.get_args(temp_dir, env, db_ctx, 0, style),
693            style,
694            true,
695        );
696    }
697
698    #[test]
699    fn test_get_args_with_grpc_style() {
700        let temp_dir = tempfile::tempdir().unwrap();
701        let (env, db_ctx) = test_env(temp_dir.path());
702        test_all_modes(&env, &db_ctx, temp_dir.path(), GrpcArgStyle::Grpc);
703    }
704
705    #[test]
706    fn test_get_args_with_rpc_style() {
707        let temp_dir = tempfile::tempdir().unwrap();
708        let (env, db_ctx) = test_env(temp_dir.path());
709        test_all_modes(&env, &db_ctx, temp_dir.path(), GrpcArgStyle::Rpc);
710    }
711
712    #[test]
713    fn test_arg_style_for_unknown_version_defaults_to_grpc() {
714        assert_eq!(GrpcArgStyle::for_version(None), GrpcArgStyle::Grpc);
715    }
716
717    #[test]
718    fn test_arg_style_for_legacy_versions_uses_rpc() {
719        let v1_0_0 = Version::parse("v1.0.0").unwrap();
720        let v1_0_9 = Version::parse("v1.0.9").unwrap();
721
722        assert_eq!(GrpcArgStyle::for_version(Some(&v1_0_0)), GrpcArgStyle::Rpc);
723        assert_eq!(GrpcArgStyle::for_version(Some(&v1_0_9)), GrpcArgStyle::Rpc);
724    }
725
726    #[test]
727    fn test_arg_style_for_current_versions_uses_grpc() {
728        let v1_1_0 = Version::parse("v1.1.0").unwrap();
729        let v1_2_0 = Version::parse("v1.2.0").unwrap();
730
731        assert_eq!(GrpcArgStyle::for_version(Some(&v1_1_0)), GrpcArgStyle::Grpc);
732        assert_eq!(GrpcArgStyle::for_version(Some(&v1_2_0)), GrpcArgStyle::Grpc);
733    }
734
735    #[test]
736    fn test_generate_distributed_gc_config_when_enabled() {
737        let temp_dir = tempfile::tempdir().unwrap();
738        let store_config = StoreConfig {
739            store_addrs: vec![],
740            setup_etcd: false,
741            setup_pg: None,
742            setup_mysql: None,
743            enable_flat_format: false,
744            enable_gc: true,
745        };
746        let db_ctx = GreptimeDBContext::new(WalConfig::RaftEngine, store_config);
747        let metasrv = ServerMode::Metasrv {
748            rpc_bind_addr: "127.0.0.1:4201".to_string(),
749            rpc_server_addr: "127.0.0.1:4201".to_string(),
750            http_addr: "127.0.0.1:4200".to_string(),
751        };
752        let datanode = ServerMode::Datanode {
753            rpc_bind_addr: "127.0.0.1:4301".to_string(),
754            rpc_server_addr: "127.0.0.1:4301".to_string(),
755            http_addr: "127.0.0.1:4300".to_string(),
756            metasrv_addr: "127.0.0.1:4201".to_string(),
757            node_id: 0,
758        };
759
760        let metasrv_config =
761            std::fs::read_to_string(metasrv.generate_config_file(temp_dir.path(), &db_ctx, 0))
762                .unwrap();
763        let datanode_config =
764            std::fs::read_to_string(datanode.generate_config_file(temp_dir.path(), &db_ctx, 0))
765                .unwrap();
766
767        assert!(metasrv_config.contains("[gc]\nenable = true"));
768        assert!(metasrv_config.contains("[gc.experimental_soft_drop]\nenable = true"));
769        assert!(datanode_config.contains("[region_engine.mito.gc]\nenable = true"));
770    }
771}