1use 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::{CompatConfigStage, 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum GrpcArgStyle {
37 Grpc,
39 Rpc,
41}
42
43impl GrpcArgStyle {
44 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 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 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 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 copy_root: String,
145 procedure_dir: String,
146 is_raft_engine: bool,
147 kafka_wal_broker_endpoints: String,
148 use_etcd: bool,
149 store_addrs: String,
150 instance_id: usize,
151 addrs: HashMap<String, String>,
152 enable_flat_format: bool,
154 enable_gc: bool,
156}
157
158impl ServerMode {
159 pub fn random_standalone() -> Self {
160 let http_port = get_unique_random_port();
161 let rpc_port = get_unique_random_port();
162 let mysql_port = get_unique_random_port();
163 let postgres_port = get_unique_random_port();
164
165 ServerMode::Standalone {
166 http_addr: format!("127.0.0.1:{http_port}"),
167 rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
168 mysql_addr: format!("127.0.0.1:{mysql_port}"),
169 postgres_addr: format!("127.0.0.1:{postgres_port}"),
170 }
171 }
172
173 pub fn random_frontend(metasrv_port: u16) -> Self {
174 let http_port = get_unique_random_port();
175 let rpc_port = get_unique_random_port();
176 let mysql_port = get_unique_random_port();
177 let postgres_port = get_unique_random_port();
178
179 ServerMode::Frontend {
180 http_addr: format!("127.0.0.1:{http_port}"),
181 rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
182 mysql_addr: format!("127.0.0.1:{mysql_port}"),
183 postgres_addr: format!("127.0.0.1:{postgres_port}"),
184 metasrv_addr: format!("127.0.0.1:{metasrv_port}"),
185 }
186 }
187
188 pub fn random_metasrv() -> Self {
189 let bind_port = get_unique_random_port();
190 let http_port = get_unique_random_port();
191
192 ServerMode::Metasrv {
193 rpc_bind_addr: format!("127.0.0.1:{bind_port}"),
194 rpc_server_addr: format!("127.0.0.1:{bind_port}"),
195 http_addr: format!("127.0.0.1:{http_port}"),
196 }
197 }
198
199 pub fn random_datanode(metasrv_port: u16, node_id: u32) -> Self {
200 let rpc_port = get_unique_random_port();
201 let http_port = get_unique_random_port();
202
203 ServerMode::Datanode {
204 rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
205 rpc_server_addr: format!("127.0.0.1:{rpc_port}"),
206 http_addr: format!("127.0.0.1:{http_port}"),
207 metasrv_addr: format!("127.0.0.1:{metasrv_port}"),
208 node_id,
209 }
210 }
211
212 pub fn random_flownode(metasrv_port: u16, node_id: u32) -> Self {
213 let rpc_port = get_unique_random_port();
214 let http_port = get_unique_random_port();
215
216 ServerMode::Flownode {
217 rpc_bind_addr: format!("127.0.0.1:{rpc_port}"),
218 rpc_server_addr: format!("127.0.0.1:{rpc_port}"),
219 http_addr: format!("127.0.0.1:{http_port}"),
220 metasrv_addr: format!("127.0.0.1:{metasrv_port}"),
221 node_id,
222 }
223 }
224
225 pub fn name(&self) -> &'static str {
226 match self {
227 ServerMode::Standalone { .. } => "standalone",
228 ServerMode::Frontend { .. } => "frontend",
229 ServerMode::Metasrv { .. } => "metasrv",
230 ServerMode::Datanode { .. } => "datanode",
231 ServerMode::Flownode { .. } => "flownode",
232 }
233 }
234
235 pub fn check_addrs(&self) -> Vec<String> {
237 match self {
238 ServerMode::Standalone {
239 rpc_bind_addr,
240 mysql_addr,
241 postgres_addr,
242 http_addr,
243 ..
244 } => {
245 vec![
246 rpc_bind_addr.clone(),
247 mysql_addr.clone(),
248 postgres_addr.clone(),
249 http_addr.clone(),
250 ]
251 }
252 ServerMode::Frontend {
253 rpc_bind_addr,
254 mysql_addr,
255 postgres_addr,
256 ..
257 } => {
258 vec![
259 rpc_bind_addr.clone(),
260 mysql_addr.clone(),
261 postgres_addr.clone(),
262 ]
263 }
264 ServerMode::Metasrv { rpc_bind_addr, .. } => {
265 vec![rpc_bind_addr.clone()]
266 }
267 ServerMode::Datanode { rpc_bind_addr, .. } => {
268 vec![rpc_bind_addr.clone()]
269 }
270 ServerMode::Flownode { rpc_bind_addr, .. } => {
271 vec![rpc_bind_addr.clone()]
272 }
273 }
274 }
275
276 pub fn server_addr(&self) -> Option<ServerAddr> {
278 match self {
279 ServerMode::Standalone {
280 rpc_bind_addr,
281 mysql_addr,
282 postgres_addr,
283 ..
284 } => Some(ServerAddr {
285 server_addr: Some(rpc_bind_addr.clone()),
286 pg_server_addr: Some(postgres_addr.clone()),
287 mysql_server_addr: Some(mysql_addr.clone()),
288 }),
289 ServerMode::Frontend {
290 rpc_bind_addr,
291 mysql_addr,
292 postgres_addr,
293 ..
294 } => Some(ServerAddr {
295 server_addr: Some(rpc_bind_addr.clone()),
296 pg_server_addr: Some(postgres_addr.clone()),
297 mysql_server_addr: Some(mysql_addr.clone()),
298 }),
299 _ => None,
300 }
301 }
302
303 pub fn generate_config_file(
304 &self,
305 sqlness_home: &Path,
306 db_ctx: &GreptimeDBContext,
307 id: usize,
308 compat_stage: &CompatConfigStage,
309 ) -> String {
310 let mut tt = TinyTemplate::new();
311
312 let mut path = util::sqlness_conf_path();
313 path.push(format!("{}-test.toml.template", self.name()));
314 let template = std::fs::read_to_string(&path)
315 .unwrap_or_else(|e| panic!("read file '{}' error: {e}", path.display()));
316 tt.add_template(self.name(), &template).unwrap();
317
318 let data_home = sqlness_home.join(format!("greptimedb-{}-{}", id, self.name()));
319 std::fs::create_dir_all(data_home.as_path()).unwrap();
320
321 let wal_dir = data_home.join("wal").display().to_string();
322 let procedure_dir = data_home.join("procedure").display().to_string();
323
324 let addrs: HashMap<String, String> = match self {
326 ServerMode::Standalone {
327 rpc_bind_addr,
328 mysql_addr,
329 postgres_addr,
330 http_addr,
331 } => [
332 ("http_addr".to_string(), http_addr.clone()),
333 ("grpc_addr".to_string(), rpc_bind_addr.clone()),
334 ("mysql_addr".to_string(), mysql_addr.clone()),
335 ("postgres_addr".to_string(), postgres_addr.clone()),
336 ]
337 .into(),
338 ServerMode::Frontend { rpc_bind_addr, .. } => {
339 [("grpc_addr".to_string(), rpc_bind_addr.clone())].into()
340 }
341 ServerMode::Datanode { metasrv_addr, .. } => {
342 [("metasrv_addr".to_string(), metasrv_addr.clone())].into()
343 }
344 _ => HashMap::new(),
345 };
346
347 let ctx = ConfigContext {
348 wal_dir,
349 data_home: data_home.display().to_string(),
350 copy_root: sqlness_home.join("copy").display().to_string(),
351 procedure_dir,
352 is_raft_engine: db_ctx.is_raft_engine(),
353 kafka_wal_broker_endpoints: db_ctx.kafka_wal_broker_endpoints(),
354 use_etcd: !db_ctx.store_config().store_addrs.is_empty(),
355 store_addrs: db_ctx
356 .store_config()
357 .store_addrs
358 .iter()
359 .map(|p| format!("\"{p}\""))
360 .collect::<Vec<_>>()
361 .join(","),
362 instance_id: id,
363 addrs,
364 enable_flat_format: db_ctx.store_config().enable_flat_format,
365 enable_gc: db_ctx.store_config().enable_gc,
366 };
367
368 let rendered = tt.render(self.name(), &ctx).unwrap();
369 let rendered = match (self, compat_stage) {
370 (ServerMode::Datanode { .. }, CompatConfigStage::Old(overlay)) => overlay
371 .apply_to_rendered_baseline(&rendered)
372 .unwrap_or_else(|error| {
373 panic!(
374 "Failed to apply old datanode overlay {}: {error}",
375 overlay.source().display()
376 )
377 }),
378 _ => rendered,
379 };
380
381 let stage_suffix = match compat_stage {
382 CompatConfigStage::Baseline => "baseline",
383 CompatConfigStage::Old(_) => "old",
384 CompatConfigStage::Current => "current",
385 };
386 let conf_file = data_home
387 .join(if matches!(self, ServerMode::Datanode { .. }) {
388 format!(
389 "{}-{}-{}-{stage_suffix}.toml",
390 self.name(),
391 id,
392 db_ctx.time()
393 )
394 } else {
395 format!("{}-{}-{}.toml", self.name(), id, db_ctx.time())
396 })
397 .display()
398 .to_string();
399 println!(
400 "Generating id {}, {} config file in {conf_file}",
401 id,
402 self.name()
403 );
404 std::fs::write(&conf_file, rendered).unwrap();
405
406 conf_file
407 }
408
409 pub fn get_args(
410 &self,
411 sqlness_home: &Path,
412 env: &Env,
413 db_ctx: &GreptimeDBContext,
414 id: usize,
415 arg_style: GrpcArgStyle,
416 compat_stage: &CompatConfigStage,
417 ) -> Vec<String> {
418 let mut args = env
419 .extra_args()
420 .iter()
421 .map(String::as_str)
422 .chain([DEFAULT_LOG_LEVEL, self.name(), "start"])
423 .map(ToString::to_string)
424 .collect::<Vec<String>>();
425
426 match self {
427 ServerMode::Standalone {
428 http_addr,
429 rpc_bind_addr,
430 mysql_addr,
431 postgres_addr,
432 } => {
433 args.extend([
434 format!(
435 "--log-dir={}/greptimedb-{}-standalone/logs",
436 sqlness_home.display(),
437 id
438 ),
439 "-c".to_string(),
440 self.generate_config_file(sqlness_home, db_ctx, id, compat_stage),
441 format!("--http-addr={http_addr}"),
442 format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
443 format!("--mysql-addr={mysql_addr}"),
444 format!("--postgres-addr={postgres_addr}"),
445 ]);
446 }
447 ServerMode::Frontend {
448 http_addr,
449 rpc_bind_addr,
450 mysql_addr,
451 postgres_addr,
452 metasrv_addr,
453 } => {
454 args.extend([
455 format!("--metasrv-addrs={metasrv_addr}"),
456 format!("--http-addr={http_addr}"),
457 format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
458 format!("{}={rpc_bind_addr}", arg_style.server_addr_arg()),
461 format!("--mysql-addr={mysql_addr}"),
462 format!("--postgres-addr={postgres_addr}"),
463 format!(
464 "--log-dir={}/greptimedb-{}-frontend/logs",
465 sqlness_home.display(),
466 id
467 ),
468 "-c".to_string(),
469 self.generate_config_file(sqlness_home, db_ctx, id, compat_stage),
470 ]);
471 }
472 ServerMode::Metasrv {
473 rpc_bind_addr,
474 rpc_server_addr,
475 http_addr,
476 } => {
477 args.extend([
478 arg_style.bind_addr_arg().to_string(),
479 rpc_bind_addr.clone(),
480 arg_style.server_addr_arg().to_string(),
481 rpc_server_addr.clone(),
482 "--enable-region-failover".to_string(),
483 "false".to_string(),
484 format!("--http-addr={http_addr}"),
485 format!(
486 "--log-dir={}/greptimedb-{}-metasrv/logs",
487 sqlness_home.display(),
488 id
489 ),
490 "-c".to_string(),
491 self.generate_config_file(sqlness_home, db_ctx, id, compat_stage),
492 ]);
493
494 if matches!(
495 db_ctx.store_config().setup_pg,
496 Some(ServiceProvider::Create)
497 ) {
498 let client_ports = db_ctx
499 .store_config()
500 .store_addrs
501 .iter()
502 .map(|s| s.split(':').nth(1).unwrap().parse::<u16>().unwrap())
503 .collect::<Vec<_>>();
504 let client_port = client_ports.first().unwrap_or(&5432);
505 let pg_server_addr = format!(
506 "postgresql://greptimedb:admin@127.0.0.1:{}/postgres",
507 client_port
508 );
509 args.extend(vec!["--backend".to_string(), "postgres-store".to_string()]);
510 args.extend(vec!["--store-addrs".to_string(), pg_server_addr]);
511 } else if let Some(ServiceProvider::External(connection_string)) =
512 db_ctx.store_config().setup_pg
513 {
514 println!("Using external PostgreSQL '{connection_string}' as Kvbackend");
515 args.extend([
516 "--backend".to_string(),
517 "postgres-store".to_string(),
518 "--store-addrs".to_string(),
519 connection_string,
520 ]);
521 } else if matches!(
522 db_ctx.store_config().setup_mysql,
523 Some(ServiceProvider::Create)
524 ) {
525 let client_ports = db_ctx
526 .store_config()
527 .store_addrs
528 .iter()
529 .map(|s| s.split(':').nth(1).unwrap().parse::<u16>().unwrap())
530 .collect::<Vec<_>>();
531 let client_port = client_ports.first().unwrap_or(&3306);
532 let mysql_server_addr =
533 format!("mysql://greptimedb:admin@127.0.0.1:{}/mysql", client_port);
534 args.extend(vec!["--backend".to_string(), "mysql-store".to_string()]);
535 args.extend(vec!["--store-addrs".to_string(), mysql_server_addr]);
536 } else if let Some(ServiceProvider::External(connection_string)) =
537 db_ctx.store_config().setup_mysql
538 {
539 println!("Using external MySQL '{connection_string}' as Kvbackend");
540 args.extend([
541 "--backend".to_string(),
542 "mysql-store".to_string(),
543 "--store-addrs".to_string(),
544 connection_string,
545 ]);
546 } else if db_ctx.store_config().store_addrs.is_empty() {
547 args.extend(vec!["--backend".to_string(), "memory-store".to_string()])
548 }
549 }
550 ServerMode::Datanode {
551 rpc_bind_addr,
552 rpc_server_addr,
553 http_addr,
554 metasrv_addr,
555 node_id,
556 } => {
557 let data_home = sqlness_home.join(format!(
558 "greptimedb_{}_datanode_{}_{node_id}",
559 id,
560 db_ctx.time()
561 ));
562 args.extend([
563 format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
564 format!("{}={rpc_server_addr}", arg_style.server_addr_arg()),
565 format!("--http-addr={http_addr}"),
566 format!("--data-home={}", data_home.display()),
567 format!("--log-dir={}/logs", data_home.display()),
568 format!("--node-id={node_id}"),
569 "-c".to_string(),
570 self.generate_config_file(sqlness_home, db_ctx, id, compat_stage),
571 format!("--metasrv-addrs={metasrv_addr}"),
572 ]);
573 }
574 ServerMode::Flownode {
575 rpc_bind_addr,
576 rpc_server_addr,
577 http_addr,
578 metasrv_addr,
579 node_id,
580 } => {
581 args.extend([
582 format!("{}={rpc_bind_addr}", arg_style.bind_addr_arg()),
583 format!("{}={rpc_server_addr}", arg_style.server_addr_arg()),
584 format!("--node-id={node_id}"),
585 format!(
586 "--log-dir={}/greptimedb-{}-flownode/logs",
587 sqlness_home.display(),
588 id
589 ),
590 format!("--metasrv-addrs={metasrv_addr}"),
591 format!("--http-addr={http_addr}"),
592 ]);
593 }
594 }
595
596 args
597 }
598}
599
600#[cfg(test)]
601mod tests {
602 use std::path::PathBuf;
603 use std::sync::Arc;
604
605 use super::*;
606 use crate::cmd::datanode_overlay::{DatanodeOverlay, DatanodeProtectionPolicy};
607 use crate::env::bare::{StoreConfig, WalConfig};
608
609 fn test_env(sqlness_home: &Path) -> (Env, GreptimeDBContext) {
610 let store_config = StoreConfig {
611 store_addrs: vec!["127.0.0.1:2379".to_string()],
612 setup_etcd: true,
613 setup_pg: None,
614 setup_mysql: None,
615 enable_flat_format: false,
616 enable_gc: false,
617 };
618 let env = Env::new(
619 sqlness_home.to_path_buf(),
620 ServerAddr::default(),
621 WalConfig::RaftEngine,
622 false,
623 Some(PathBuf::from(".")),
624 store_config.clone(),
625 vec![],
626 );
627 let db_ctx = GreptimeDBContext::new(WalConfig::RaftEngine, store_config);
628
629 (env, db_ctx)
630 }
631
632 fn old_stage(temp_dir: &Path) -> CompatConfigStage {
633 std::fs::write(
634 temp_dir.join("old-datanode.toml"),
635 "old_only = \"applied\"\nmode = \"standalone\"\n",
636 )
637 .unwrap();
638 let overlay = DatanodeOverlay::load(temp_dir, Path::new("old-datanode.toml"))
639 .unwrap()
640 .prepare(&DatanodeProtectionPolicy::for_wal(&WalConfig::RaftEngine))
641 .unwrap();
642 CompatConfigStage::Old(Arc::new(overlay))
643 }
644
645 fn has_arg(args: &[String], name: &str) -> bool {
646 let prefix = format!("{name}=");
647 args.iter()
648 .any(|arg| arg == name || arg.starts_with(&prefix))
649 }
650
651 fn assert_uses_style(args: &[String], style: GrpcArgStyle, expect_server_addr: bool) {
652 let bind = style.bind_addr_arg();
653 let server = style.server_addr_arg();
654 let other_bind = match style {
655 GrpcArgStyle::Grpc => "--rpc-bind-addr",
656 GrpcArgStyle::Rpc => "--grpc-bind-addr",
657 };
658 let other_server = match style {
659 GrpcArgStyle::Grpc => "--rpc-server-addr",
660 GrpcArgStyle::Rpc => "--grpc-server-addr",
661 };
662
663 assert!(has_arg(args, bind), "missing {bind} in args: {args:?}");
664 assert!(
665 !args.iter().any(|arg| arg.contains(other_bind)),
666 "unexpected {other_bind} in args: {args:?}"
667 );
668
669 if expect_server_addr {
670 assert!(has_arg(args, server), "missing {server} in args: {args:?}");
671 assert!(
672 !args.iter().any(|arg| arg.contains(other_server)),
673 "unexpected {other_server} in args: {args:?}"
674 );
675 }
676 }
677
678 fn test_all_modes(env: &Env, db_ctx: &GreptimeDBContext, temp_dir: &Path, style: GrpcArgStyle) {
679 let standalone = ServerMode::Standalone {
680 http_addr: "127.0.0.1:4000".to_string(),
681 rpc_bind_addr: "127.0.0.1:4001".to_string(),
682 mysql_addr: "127.0.0.1:4002".to_string(),
683 postgres_addr: "127.0.0.1:4003".to_string(),
684 };
685 assert_uses_style(
686 &standalone.get_args(
687 temp_dir,
688 env,
689 db_ctx,
690 0,
691 style,
692 &CompatConfigStage::Baseline,
693 ),
694 style,
695 false,
696 );
697
698 let frontend = ServerMode::Frontend {
699 http_addr: "127.0.0.1:4100".to_string(),
700 rpc_bind_addr: "127.0.0.1:4101".to_string(),
701 mysql_addr: "127.0.0.1:4102".to_string(),
702 postgres_addr: "127.0.0.1:4103".to_string(),
703 metasrv_addr: "127.0.0.1:4001".to_string(),
704 };
705 assert_uses_style(
706 &frontend.get_args(
707 temp_dir,
708 env,
709 db_ctx,
710 0,
711 style,
712 &CompatConfigStage::Baseline,
713 ),
714 style,
715 true,
716 );
717
718 let metasrv = ServerMode::Metasrv {
719 rpc_bind_addr: "127.0.0.1:4201".to_string(),
720 rpc_server_addr: "127.0.0.1:4201".to_string(),
721 http_addr: "127.0.0.1:4200".to_string(),
722 };
723 assert_uses_style(
724 &metasrv.get_args(
725 temp_dir,
726 env,
727 db_ctx,
728 0,
729 style,
730 &CompatConfigStage::Baseline,
731 ),
732 style,
733 true,
734 );
735
736 let datanode = ServerMode::Datanode {
737 rpc_bind_addr: "127.0.0.1:4301".to_string(),
738 rpc_server_addr: "127.0.0.1:4301".to_string(),
739 http_addr: "127.0.0.1:4300".to_string(),
740 metasrv_addr: "127.0.0.1:4001".to_string(),
741 node_id: 0,
742 };
743 assert_uses_style(
744 &datanode.get_args(
745 temp_dir,
746 env,
747 db_ctx,
748 0,
749 style,
750 &CompatConfigStage::Baseline,
751 ),
752 style,
753 true,
754 );
755
756 let flownode = ServerMode::Flownode {
757 rpc_bind_addr: "127.0.0.1:4401".to_string(),
758 rpc_server_addr: "127.0.0.1:4401".to_string(),
759 http_addr: "127.0.0.1:4400".to_string(),
760 metasrv_addr: "127.0.0.1:4001".to_string(),
761 node_id: 0,
762 };
763 assert_uses_style(
764 &flownode.get_args(
765 temp_dir,
766 env,
767 db_ctx,
768 0,
769 style,
770 &CompatConfigStage::Baseline,
771 ),
772 style,
773 true,
774 );
775 }
776
777 #[test]
778 fn test_get_args_with_grpc_style() {
779 let temp_dir = tempfile::tempdir().unwrap();
780 let (env, db_ctx) = test_env(temp_dir.path());
781 test_all_modes(&env, &db_ctx, temp_dir.path(), GrpcArgStyle::Grpc);
782 }
783
784 #[test]
785 fn test_get_args_with_rpc_style() {
786 let temp_dir = tempfile::tempdir().unwrap();
787 let (env, db_ctx) = test_env(temp_dir.path());
788 test_all_modes(&env, &db_ctx, temp_dir.path(), GrpcArgStyle::Rpc);
789 }
790
791 #[test]
792 fn test_arg_style_for_unknown_version_defaults_to_grpc() {
793 assert_eq!(GrpcArgStyle::for_version(None), GrpcArgStyle::Grpc);
794 }
795
796 #[test]
797 fn test_arg_style_for_legacy_versions_uses_rpc() {
798 let v1_0_0 = Version::parse("v1.0.0").unwrap();
799 let v1_0_9 = Version::parse("v1.0.9").unwrap();
800
801 assert_eq!(GrpcArgStyle::for_version(Some(&v1_0_0)), GrpcArgStyle::Rpc);
802 assert_eq!(GrpcArgStyle::for_version(Some(&v1_0_9)), GrpcArgStyle::Rpc);
803 }
804
805 #[test]
806 fn test_arg_style_for_current_versions_uses_grpc() {
807 let v1_1_0 = Version::parse("v1.1.0").unwrap();
808 let v1_2_0 = Version::parse("v1.2.0").unwrap();
809
810 assert_eq!(GrpcArgStyle::for_version(Some(&v1_1_0)), GrpcArgStyle::Grpc);
811 assert_eq!(GrpcArgStyle::for_version(Some(&v1_2_0)), GrpcArgStyle::Grpc);
812 }
813
814 #[test]
815 fn test_generate_distributed_gc_config_when_enabled() {
816 let temp_dir = tempfile::tempdir().unwrap();
817 let store_config = StoreConfig {
818 store_addrs: vec![],
819 setup_etcd: false,
820 setup_pg: None,
821 setup_mysql: None,
822 enable_flat_format: false,
823 enable_gc: true,
824 };
825 let db_ctx = GreptimeDBContext::new(WalConfig::RaftEngine, store_config);
826 let metasrv = ServerMode::Metasrv {
827 rpc_bind_addr: "127.0.0.1:4201".to_string(),
828 rpc_server_addr: "127.0.0.1:4201".to_string(),
829 http_addr: "127.0.0.1:4200".to_string(),
830 };
831 let datanode = ServerMode::Datanode {
832 rpc_bind_addr: "127.0.0.1:4301".to_string(),
833 rpc_server_addr: "127.0.0.1:4301".to_string(),
834 http_addr: "127.0.0.1:4300".to_string(),
835 metasrv_addr: "127.0.0.1:4201".to_string(),
836 node_id: 0,
837 };
838
839 let metasrv_config = std::fs::read_to_string(metasrv.generate_config_file(
840 temp_dir.path(),
841 &db_ctx,
842 0,
843 &CompatConfigStage::Baseline,
844 ))
845 .unwrap();
846 let datanode_config = std::fs::read_to_string(datanode.generate_config_file(
847 temp_dir.path(),
848 &db_ctx,
849 0,
850 &CompatConfigStage::Baseline,
851 ))
852 .unwrap();
853
854 assert!(metasrv_config.contains("[gc]\nenable = true"));
855 assert!(datanode_config.contains("[region_engine.mito.gc]\nenable = true"));
856 }
857
858 #[test]
859 fn test_datanode_rendering_applies_only_old_stage_with_distinct_filenames() {
860 let temp_dir = tempfile::tempdir().unwrap();
861 let (_, db_ctx) = test_env(temp_dir.path());
862 let datanode = ServerMode::Datanode {
863 rpc_bind_addr: "127.0.0.1:4301".to_string(),
864 rpc_server_addr: "127.0.0.1:4301".to_string(),
865 http_addr: "127.0.0.1:4300".to_string(),
866 metasrv_addr: "127.0.0.1:4201".to_string(),
867 node_id: 0,
868 };
869 let baseline_path = datanode.generate_config_file(
870 temp_dir.path(),
871 &db_ctx,
872 0,
873 &CompatConfigStage::Baseline,
874 );
875 let old_path =
876 datanode.generate_config_file(temp_dir.path(), &db_ctx, 0, &old_stage(temp_dir.path()));
877 let current_path =
878 datanode.generate_config_file(temp_dir.path(), &db_ctx, 0, &CompatConfigStage::Current);
879
880 let baseline = std::fs::read_to_string(&baseline_path).unwrap();
881 let old = std::fs::read_to_string(&old_path).unwrap();
882 let current = std::fs::read_to_string(¤t_path).unwrap();
883 assert!(baseline_path.ends_with("-baseline.toml"));
884 assert!(old_path.ends_with("-old.toml"));
885 assert!(current_path.ends_with("-current.toml"));
886 assert!(!baseline.contains("old_only"));
887 assert!(old.contains("old_only = \"applied\""));
888 assert!(!current.contains("old_only"));
889 assert_eq!(
890 toml::from_str::<toml::Value>(&old).unwrap()["mode"].as_str(),
891 toml::from_str::<toml::Value>(&baseline).unwrap()["mode"].as_str()
892 );
893 }
894
895 #[test]
896 fn test_non_datanode_ignores_old_overlay() {
897 let temp_dir = tempfile::tempdir().unwrap();
898 let (_, db_ctx) = test_env(temp_dir.path());
899 let metasrv = ServerMode::Metasrv {
900 rpc_bind_addr: "127.0.0.1:4201".to_string(),
901 rpc_server_addr: "127.0.0.1:4201".to_string(),
902 http_addr: "127.0.0.1:4200".to_string(),
903 };
904 let config_path =
905 metasrv.generate_config_file(temp_dir.path(), &db_ctx, 0, &old_stage(temp_dir.path()));
906
907 assert!(!config_path.ends_with("-old.toml"));
908 assert!(
909 !std::fs::read_to_string(config_path)
910 .unwrap()
911 .contains("old_only")
912 );
913 }
914}