1use std::fmt::Debug;
16use std::net::SocketAddr;
17use std::path::Path;
18use std::sync::Arc;
19use std::{fs, path};
20
21use async_trait::async_trait;
22use cache::{build_fundamental_cache_registry, with_default_composite_cache_registry};
23use catalog::information_schema::InformationExtensionRef;
24use catalog::kvbackend::{CatalogManagerConfiguratorRef, KvBackendCatalogManagerBuilder};
25use catalog::process_manager::ProcessManager;
26use clap::Parser;
27use common_base::Plugins;
28use common_catalog::consts::{MIN_USER_FLOW_ID, MIN_USER_TABLE_ID};
29use common_config::{Configurable, metadata_store_dir};
30use common_error::ext::BoxedError;
31use common_meta::cache::LayeredCacheRegistryBuilder;
32use common_meta::ddl::flow_meta::FlowMetadataAllocator;
33use common_meta::ddl::table_meta::TableMetadataAllocator;
34use common_meta::ddl::{DdlContext, NoopRegionFailureDetectorControl};
35use common_meta::ddl_manager::{DdlManager, DdlManagerConfiguratorRef, DdlManagerRef};
36use common_meta::key::flow::FlowMetadataManager;
37use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
38use common_meta::kv_backend::KvBackendRef;
39use common_meta::node_manager::{FlownodeRef, NodeManagerRef};
40use common_meta::procedure_executor::{LocalProcedureExecutor, ProcedureExecutorRef};
41use common_meta::region_keeper::MemoryRegionKeeper;
42use common_meta::region_registry::LeaderRegionRegistry;
43use common_meta::sequence::{Sequence, SequenceBuilder};
44use common_meta::wal_provider::{WalProviderRef, build_wal_provider};
45use common_options::plugin_options::StandaloneFlag;
46use common_procedure::ProcedureManagerRef;
47use common_query::prelude::set_default_prefix;
48use common_telemetry::info;
49use common_telemetry::logging::{DEFAULT_LOGGING_DIR, TracingOptions};
50use common_time::timezone::set_default_timezone;
51use common_version::{short_version, verbose_version};
52use datanode::config::DatanodeOptions;
53use datanode::datanode::{Datanode, DatanodeBuilder};
54use datanode::region_server::RegionServer;
55use flow::{
56 FlownodeBuilder, FlownodeInstance, FlownodeOptions, FrontendClient, FrontendInvoker,
57 GrpcQueryHandlerWithBoxedError,
58};
59use frontend::frontend::Frontend;
60use frontend::instance::StandaloneDatanodeManager;
61use frontend::instance::builder::FrontendBuilder;
62use frontend::server::Services;
63use meta_srv::metasrv::{FLOW_ID_SEQ, TABLE_ID_SEQ};
64use plugins::PluginOptions;
65use plugins::frontend::context::{
66 CatalogManagerConfigureContext, StandaloneCatalogManagerConfigureContext,
67};
68use plugins::standalone::context::DdlManagerConfigureContext;
69use servers::tls::{TlsMode, TlsOption, merge_tls_option};
70use snafu::ResultExt;
71use standalone::options::StandaloneOptions;
72use standalone::{StandaloneInformationExtension, StandaloneRepartitionProcedureFactory};
73use tracing_appender::non_blocking::WorkerGuard;
74
75use crate::error::{OtherSnafu, Result, StartFlownodeSnafu};
76use crate::options::{GlobalOptions, GreptimeOptions};
77use crate::{App, create_resource_limit_metrics, error, log_versions, maybe_activate_heap_profile};
78
79pub const APP_NAME: &str = "greptime-standalone";
80
81#[derive(Parser)]
82pub struct Command {
83 #[clap(subcommand)]
84 subcmd: SubCommand,
85}
86
87impl Command {
88 pub async fn build(&self, opts: GreptimeOptions<StandaloneOptions>) -> Result<Instance> {
89 self.subcmd.build(opts).await
90 }
91
92 pub fn load_options(
93 &self,
94 global_options: &GlobalOptions,
95 ) -> Result<GreptimeOptions<StandaloneOptions>> {
96 self.subcmd.load_options(global_options)
97 }
98}
99
100#[derive(Parser)]
101enum SubCommand {
102 Start(StartCommand),
103}
104
105impl SubCommand {
106 async fn build(&self, opts: GreptimeOptions<StandaloneOptions>) -> Result<Instance> {
107 match self {
108 SubCommand::Start(cmd) => cmd.build(opts).await,
109 }
110 }
111
112 fn load_options(
113 &self,
114 global_options: &GlobalOptions,
115 ) -> Result<GreptimeOptions<StandaloneOptions>> {
116 match self {
117 SubCommand::Start(cmd) => cmd.load_options(global_options),
118 }
119 }
120}
121
122pub struct Instance {
123 datanode: Datanode,
124 frontend: Frontend,
125 flownode: FlownodeInstance,
126 procedure_manager: ProcedureManagerRef,
127 wal_provider: WalProviderRef,
128 leader_services_controller: Box<dyn StandaloneLeaderServicesController>,
129 _guard: Vec<WorkerGuard>,
131}
132
133impl Instance {
134 pub fn server_addr(&self, name: &str) -> Option<SocketAddr> {
136 self.frontend.server_handlers().addr(name)
137 }
138
139 pub fn mut_frontend(&mut self) -> &mut Frontend {
142 &mut self.frontend
143 }
144
145 pub fn datanode(&self) -> &Datanode {
148 &self.datanode
149 }
150}
151
152#[async_trait]
153impl App for Instance {
154 fn name(&self) -> &str {
155 APP_NAME
156 }
157
158 async fn start(&mut self) -> Result<()> {
159 self.datanode.start_telemetry();
160
161 self.leader_services_controller
162 .start(
163 self.procedure_manager.clone(),
164 self.wal_provider.clone(),
165 self.datanode.region_server(),
166 )
167 .await?;
168
169 plugins::start_frontend_plugins(self.frontend.instance.plugins().clone())
170 .await
171 .context(error::StartFrontendSnafu)?;
172
173 self.frontend
174 .start()
175 .await
176 .context(error::StartFrontendSnafu)?;
177
178 self.flownode.start().await.context(StartFlownodeSnafu)?;
179
180 Ok(())
181 }
182
183 async fn stop(&mut self) -> Result<()> {
184 self.frontend
185 .shutdown()
186 .await
187 .context(error::ShutdownFrontendSnafu)?;
188
189 self.leader_services_controller
190 .stop(
191 self.procedure_manager.clone(),
192 self.datanode.region_server(),
193 )
194 .await?;
195
196 self.datanode
197 .shutdown()
198 .await
199 .context(error::ShutdownDatanodeSnafu)?;
200
201 self.flownode
202 .shutdown()
203 .await
204 .context(error::ShutdownFlownodeSnafu)?;
205
206 info!("Datanode instance stopped.");
207
208 Ok(())
209 }
210}
211
212#[derive(Debug, Default, Parser)]
213pub struct StartCommand {
214 #[clap(long)]
215 http_addr: Option<String>,
216 #[clap(long = "grpc-bind-addr", alias = "rpc-bind-addr", alias = "rpc-addr")]
217 grpc_bind_addr: Option<String>,
218 #[clap(long)]
219 mysql_addr: Option<String>,
220 #[clap(long)]
221 postgres_addr: Option<String>,
222 #[clap(short, long)]
223 influxdb_enable: bool,
224 #[clap(short, long)]
225 pub config_file: Option<String>,
226 #[clap(long)]
227 tls_mode: Option<TlsMode>,
228 #[clap(long)]
229 tls_cert_path: Option<String>,
230 #[clap(long)]
231 tls_key_path: Option<String>,
232 #[clap(long)]
233 tls_watch: bool,
234 #[clap(long)]
235 user_provider: Option<String>,
236 #[clap(long, default_value = "GREPTIMEDB_STANDALONE")]
237 pub env_prefix: String,
238 #[clap(long)]
240 data_home: Option<String>,
241}
242
243impl StartCommand {
244 pub fn load_options(
246 &self,
247 global_options: &GlobalOptions,
248 ) -> Result<GreptimeOptions<StandaloneOptions>> {
249 let mut opts = GreptimeOptions::<StandaloneOptions>::load_layered_options(
250 self.config_file.as_deref(),
251 self.env_prefix.as_ref(),
252 )
253 .context(error::LoadLayeredConfigSnafu)?;
254
255 self.merge_with_cli_options(global_options, &mut opts.component)?;
256 opts.component.sanitize();
257
258 Ok(opts)
259 }
260
261 pub fn merge_with_cli_options(
263 &self,
264 global_options: &GlobalOptions,
265 opts: &mut StandaloneOptions,
266 ) -> Result<()> {
267 if let Some(dir) = &global_options.log_dir {
268 opts.logging.dir.clone_from(dir);
269 }
270
271 if global_options.log_level.is_some() {
272 opts.logging.level.clone_from(&global_options.log_level);
273 }
274
275 opts.tracing = TracingOptions {
276 #[cfg(feature = "tokio-console")]
277 tokio_console_addr: global_options.tokio_console_addr.clone(),
278 };
279
280 let tls_opts = TlsOption::new(
281 self.tls_mode,
282 self.tls_cert_path.clone(),
283 self.tls_key_path.clone(),
284 self.tls_watch,
285 );
286
287 if let Some(addr) = &self.http_addr {
288 opts.http.addr.clone_from(addr);
289 }
290
291 if let Some(data_home) = &self.data_home {
292 opts.storage.data_home.clone_from(data_home);
293 }
294
295 if opts.logging.dir.is_empty() {
297 opts.logging.dir = Path::new(&opts.storage.data_home)
298 .join(DEFAULT_LOGGING_DIR)
299 .to_string_lossy()
300 .to_string();
301 }
302
303 if let Some(addr) = &self.grpc_bind_addr {
304 let datanode_grpc_addr = DatanodeOptions::default().grpc.bind_addr;
306 if addr.eq(&datanode_grpc_addr) {
307 return error::IllegalConfigSnafu {
308 msg: format!(
309 "gRPC listen address conflicts with datanode reserved gRPC addr: {datanode_grpc_addr}",
310 ),
311 }.fail();
312 }
313 opts.grpc.bind_addr.clone_from(addr);
314 opts.grpc.tls = merge_tls_option(&opts.grpc.tls, tls_opts.clone());
315 }
316
317 if let Some(addr) = &self.mysql_addr {
318 opts.mysql.enable = true;
319 opts.mysql.addr.clone_from(addr);
320 opts.mysql.tls = merge_tls_option(&opts.mysql.tls, tls_opts.clone());
321 }
322
323 if let Some(addr) = &self.postgres_addr {
324 opts.postgres.enable = true;
325 opts.postgres.addr.clone_from(addr);
326 opts.postgres.tls = merge_tls_option(&opts.postgres.tls, tls_opts.clone());
327 }
328
329 if self.influxdb_enable {
330 opts.influxdb.enable = self.influxdb_enable;
331 }
332
333 if let Some(user_provider) = &self.user_provider {
334 opts.user_provider = Some(user_provider.clone());
335 }
336
337 Ok(())
338 }
339
340 #[allow(unreachable_code)]
341 #[allow(unused_variables)]
342 #[allow(clippy::diverging_sub_expression)]
343 pub async fn build(&self, opts: GreptimeOptions<StandaloneOptions>) -> Result<Instance> {
345 common_runtime::init_global_runtimes(&opts.runtime);
346
347 let guard = common_telemetry::init_global_logging(
348 APP_NAME,
349 &opts.component.logging,
350 &opts.component.tracing,
351 None,
352 Some(&opts.component.slow_query),
353 );
354
355 log_versions(verbose_version(), short_version(), APP_NAME);
356 maybe_activate_heap_profile(&opts.component.memory);
357 create_resource_limit_metrics(APP_NAME);
358
359 info!("Standalone start command: {:#?}", self);
360 info!("Standalone options: {opts:#?}");
361
362 let (mut instance, _) =
363 Self::build_with(opts.component, opts.plugins, InstanceCreator::default()).await?;
364 instance._guard.extend(guard);
365 Ok(instance)
366 }
367
368 pub async fn build_with(
369 mut opts: StandaloneOptions,
370 plugin_opts: Vec<PluginOptions>,
371 creator: InstanceCreator,
372 ) -> Result<(Instance, InstanceCreatorResult)> {
373 let mut plugins = Plugins::new();
374 plugins.insert(StandaloneFlag);
375 set_default_prefix(opts.default_column_prefix.as_deref())
376 .map_err(BoxedError::new)
377 .context(error::BuildCliSnafu)?;
378
379 opts.grpc.detect_server_addr();
380 let fe_opts = opts.frontend_options();
381 let dn_opts = opts.datanode_options();
382
383 plugins::setup_frontend_plugins(&mut plugins, &plugin_opts, &fe_opts)
384 .await
385 .context(error::StartFrontendSnafu)?;
386
387 plugins::setup_datanode_plugins(&mut plugins, &plugin_opts, &dn_opts)
388 .await
389 .context(error::StartDatanodeSnafu)?;
390
391 set_default_timezone(fe_opts.default_timezone.as_deref())
392 .context(error::InitTimezoneSnafu)?;
393
394 let data_home = &dn_opts.storage.data_home;
395 fs::create_dir_all(path::Path::new(data_home))
397 .context(error::CreateDirSnafu { dir: data_home })?;
398
399 let metadata_dir = metadata_store_dir(data_home);
400 let kv_backend = creator
401 .metadata_kv_backend_creator
402 .create(metadata_dir, &opts)
403 .await?;
404 let procedure_manager =
405 standalone::build_procedure_manager(kv_backend.clone(), opts.procedure);
406
407 plugins::setup_standalone_plugins(&mut plugins, &plugin_opts, &opts, kv_backend.clone())
408 .await
409 .context(error::SetupStandalonePluginsSnafu)?;
410
411 let layered_cache_builder = LayeredCacheRegistryBuilder::default();
413 let fundamental_cache_registry = build_fundamental_cache_registry(kv_backend.clone());
414 let mut layered_cache_builder = with_default_composite_cache_registry(
415 layered_cache_builder.add_cache_registry(fundamental_cache_registry),
416 )
417 .context(error::BuildCacheRegistrySnafu)?;
418
419 if let Some(plugin_cache_builder) = plugins::standalone::configure_cache_registry(&plugins)
420 {
421 layered_cache_builder =
422 layered_cache_builder.add_cache_registry(plugin_cache_builder.build());
423 }
424
425 let layered_cache_registry = Arc::new(layered_cache_builder.build());
426
427 let mut builder = DatanodeBuilder::new(dn_opts, plugins.clone(), kv_backend.clone());
428 builder.with_cache_registry(layered_cache_registry.clone());
429 if let Some(writable) = creator.open_regions_writable_override {
430 builder.with_open_regions_writable_override(writable);
431 }
432 let datanode = builder.build().await.context(error::StartDatanodeSnafu)?;
433
434 let information_extension = Arc::new(StandaloneInformationExtension::new(
435 datanode.region_server(),
436 procedure_manager.clone(),
437 ));
438
439 plugins.insert::<InformationExtensionRef>(information_extension.clone());
440
441 let process_manager = Arc::new(ProcessManager::new(opts.grpc.server_addr.clone(), None));
442
443 let (frontend_client, frontend_instance_handler) =
446 FrontendClient::from_empty_grpc_handler(opts.query.clone());
447 let frontend_client = Arc::new(frontend_client);
448
449 let builder = KvBackendCatalogManagerBuilder::new(
450 information_extension.clone(),
451 kv_backend.clone(),
452 layered_cache_registry.clone(),
453 )
454 .with_procedure_manager(procedure_manager.clone())
455 .with_process_manager(process_manager.clone());
456 let builder = if let Some(configurator) =
457 plugins.get::<CatalogManagerConfiguratorRef<CatalogManagerConfigureContext>>()
458 {
459 let ctx = StandaloneCatalogManagerConfigureContext {
460 fe_client: frontend_client.clone(),
461 };
462 let ctx = CatalogManagerConfigureContext::Standalone(ctx);
463 configurator
464 .configure(builder, ctx)
465 .await
466 .context(OtherSnafu)?
467 } else {
468 builder
469 };
470 let catalog_manager = builder.build();
471
472 let table_metadata_manager =
473 Self::create_table_metadata_manager(kv_backend.clone()).await?;
474
475 let flow_metadata_manager = Arc::new(FlowMetadataManager::new(kv_backend.clone()));
476 let flownode_options = FlownodeOptions {
477 flow: opts.flow.clone(),
478 ..Default::default()
479 };
480
481 let flow_builder = FlownodeBuilder::new(
482 flownode_options,
483 plugins.clone(),
484 table_metadata_manager.clone(),
485 catalog_manager.clone(),
486 flow_metadata_manager.clone(),
487 frontend_client.clone(),
488 );
489 let flownode = flow_builder
490 .build()
491 .await
492 .map_err(BoxedError::new)
493 .context(error::OtherSnafu)?;
494
495 {
497 information_extension
498 .set_flow_engine(flownode.flow_engine())
499 .await;
500 }
501
502 let node_manager = creator
503 .node_manager_creator
504 .create(
505 &kv_backend,
506 datanode.region_server(),
507 flownode.flow_engine(),
508 )
509 .await?;
510
511 let table_id_allocator = creator.table_id_allocator_creator.create(&kv_backend);
512 let flow_id_sequence = Arc::new(
513 SequenceBuilder::new(FLOW_ID_SEQ, kv_backend.clone())
514 .initial(MIN_USER_FLOW_ID as u64)
515 .step(10)
516 .build(),
517 );
518 let kafka_options = opts
519 .wal
520 .clone()
521 .try_into()
522 .context(error::InvalidWalProviderSnafu)?;
523 let wal_provider = build_wal_provider(&kafka_options, kv_backend.clone())
524 .await
525 .context(error::BuildWalProviderSnafu)?;
526 let wal_provider = Arc::new(wal_provider);
527 let table_metadata_allocator = Arc::new(TableMetadataAllocator::new(
528 table_id_allocator.clone(),
529 wal_provider.clone(),
530 ));
531 let flow_metadata_allocator = Arc::new(FlowMetadataAllocator::with_noop_peer_allocator(
532 flow_id_sequence,
533 ));
534
535 let ddl_context = DdlContext {
536 node_manager: node_manager.clone(),
537 cache_invalidator: layered_cache_registry.clone(),
538 memory_region_keeper: Arc::new(MemoryRegionKeeper::default()),
539 leader_region_registry: Arc::new(LeaderRegionRegistry::default()),
540 table_metadata_manager: table_metadata_manager.clone(),
541 table_metadata_allocator: table_metadata_allocator.clone(),
542 flow_metadata_manager: flow_metadata_manager.clone(),
543 flow_metadata_allocator: flow_metadata_allocator.clone(),
544 region_failure_detector_controller: Arc::new(NoopRegionFailureDetectorControl),
545 };
546
547 let ddl_manager = DdlManager::try_new(
548 ddl_context,
549 procedure_manager.clone(),
550 Arc::new(StandaloneRepartitionProcedureFactory),
551 true,
552 )
553 .context(error::InitDdlManagerSnafu)?;
554
555 let ddl_manager = if let Some(configurator) =
556 plugins.get::<DdlManagerConfiguratorRef<DdlManagerConfigureContext>>()
557 {
558 let ctx = DdlManagerConfigureContext {
559 kv_backend: kv_backend.clone(),
560 fe_client: frontend_client.clone(),
561 catalog_manager: catalog_manager.clone(),
562 };
563 configurator
564 .configure(ddl_manager, ctx)
565 .await
566 .context(OtherSnafu)?
567 } else {
568 ddl_manager
569 };
570
571 let procedure_executor = creator
572 .procedure_executor_creator
573 .create(Arc::new(ddl_manager), procedure_manager.clone())
574 .await?;
575
576 let fe_instance = FrontendBuilder::new(
577 fe_opts.clone(),
578 kv_backend.clone(),
579 layered_cache_registry.clone(),
580 catalog_manager.clone(),
581 node_manager.clone(),
582 procedure_executor.clone(),
583 process_manager,
584 )
585 .with_plugin(plugins.clone())
586 .try_build()
587 .await
588 .context(error::StartFrontendSnafu)?;
589 let fe_instance = Arc::new(fe_instance);
590
591 let grpc_handler = fe_instance.clone() as Arc<dyn GrpcQueryHandlerWithBoxedError>;
593 let weak_grpc_handler = Arc::downgrade(&grpc_handler);
594 frontend_instance_handler
595 .set_handler(weak_grpc_handler)
596 .await;
597
598 let flow_streaming_engine = flownode.flow_engine().streaming_engine();
600 let invoker = FrontendInvoker::build_from(
602 flow_streaming_engine.clone(),
603 catalog_manager.clone(),
604 kv_backend.clone(),
605 layered_cache_registry.clone(),
606 procedure_executor,
607 node_manager.clone(),
608 fe_instance.frontend_peer_addr().to_string(),
609 )
610 .await
611 .context(StartFlownodeSnafu)?;
612 flow_streaming_engine.set_frontend_invoker(invoker).await;
613
614 let servers = Services::new(opts, fe_instance.clone(), plugins.clone())
615 .build()
616 .context(error::StartFrontendSnafu)?;
617
618 let frontend = Frontend {
619 instance: fe_instance,
620 servers,
621 heartbeat_task: None,
622 };
623
624 let instance = Instance {
625 datanode,
626 frontend,
627 flownode,
628 procedure_manager,
629 wal_provider,
630 leader_services_controller: creator.leader_services_controller,
631 _guard: vec![],
632 };
633 let result = InstanceCreatorResult {
634 kv_backend,
635 node_manager,
636 table_id_allocator,
637 };
638 Ok((instance, result))
639 }
640
641 pub async fn create_table_metadata_manager(
642 kv_backend: KvBackendRef,
643 ) -> Result<TableMetadataManagerRef> {
644 let table_metadata_manager = Arc::new(TableMetadataManager::new(kv_backend));
645
646 table_metadata_manager
647 .init()
648 .await
649 .context(error::InitMetadataSnafu)?;
650
651 Ok(table_metadata_manager)
652 }
653}
654
655#[async_trait]
656pub trait NodeManagerCreator: Send + Sync {
657 async fn create(
658 &self,
659 kv_backend: &KvBackendRef,
660 region_server: RegionServer,
661 flow_server: FlownodeRef,
662 ) -> Result<NodeManagerRef>;
663}
664
665pub struct DefaultNodeManagerCreator;
666
667#[async_trait]
668impl NodeManagerCreator for DefaultNodeManagerCreator {
669 async fn create(
670 &self,
671 _: &KvBackendRef,
672 region_server: RegionServer,
673 flow_server: FlownodeRef,
674 ) -> Result<NodeManagerRef> {
675 Ok(Arc::new(StandaloneDatanodeManager {
676 region_server,
677 flow_server,
678 }))
679 }
680}
681
682#[async_trait]
688pub trait MetadataKvBackendCreator: Send + Sync {
689 async fn create(&self, metadata_dir: String, opts: &StandaloneOptions) -> Result<KvBackendRef>;
690}
691
692pub struct DefaultMetadataKvBackendCreator;
693
694#[async_trait]
695impl MetadataKvBackendCreator for DefaultMetadataKvBackendCreator {
696 async fn create(&self, metadata_dir: String, opts: &StandaloneOptions) -> Result<KvBackendRef> {
697 standalone::build_metadata_kvbackend(metadata_dir, opts.metadata_store)
698 .context(error::BuildMetadataKvbackendSnafu)
699 }
700}
701
702pub trait TableIdAllocatorCreator: Send + Sync {
703 fn create(&self, kv_backend: &KvBackendRef) -> Arc<Sequence>;
704}
705
706struct DefaultTableIdAllocatorCreator;
707
708impl TableIdAllocatorCreator for DefaultTableIdAllocatorCreator {
709 fn create(&self, kv_backend: &KvBackendRef) -> Arc<Sequence> {
710 Arc::new(
711 SequenceBuilder::new(TABLE_ID_SEQ, kv_backend.clone())
712 .initial(MIN_USER_TABLE_ID as u64)
713 .step(10)
714 .build(),
715 )
716 }
717}
718
719#[async_trait]
720pub trait ProcedureExecutorCreator: Send + Sync {
721 async fn create(
722 &self,
723 ddl_manager: DdlManagerRef,
724 procedure_manager: ProcedureManagerRef,
725 ) -> Result<ProcedureExecutorRef>;
726}
727
728pub struct DefaultProcedureExecutorCreator;
729
730#[async_trait]
731impl ProcedureExecutorCreator for DefaultProcedureExecutorCreator {
732 async fn create(
733 &self,
734 ddl_manager: DdlManagerRef,
735 procedure_manager: ProcedureManagerRef,
736 ) -> Result<ProcedureExecutorRef> {
737 Ok(Arc::new(LocalProcedureExecutor::new(
738 ddl_manager,
739 procedure_manager,
740 )))
741 }
742}
743
744#[async_trait]
745pub trait StandaloneLeaderServicesController: Send + Sync {
746 async fn start(
751 &self,
752 procedure_manager: ProcedureManagerRef,
753 wal_provider: WalProviderRef,
754 region_server: RegionServer,
755 ) -> Result<()>;
756
757 async fn stop(
759 &self,
760 procedure_manager: ProcedureManagerRef,
761 region_server: RegionServer,
762 ) -> Result<()>;
763}
764
765pub struct DefaultStandaloneLeaderServicesController;
766
767#[async_trait]
768impl StandaloneLeaderServicesController for DefaultStandaloneLeaderServicesController {
769 async fn start(
770 &self,
771 procedure_manager: ProcedureManagerRef,
772 wal_provider: WalProviderRef,
773 _region_server: RegionServer,
774 ) -> Result<()> {
775 procedure_manager
776 .start()
777 .await
778 .context(error::StartProcedureManagerSnafu)?;
779 wal_provider
780 .start()
781 .await
782 .context(error::StartWalProviderSnafu)
783 }
784
785 async fn stop(
786 &self,
787 procedure_manager: ProcedureManagerRef,
788 _region_server: RegionServer,
789 ) -> Result<()> {
790 procedure_manager
791 .stop()
792 .await
793 .context(error::StopProcedureManagerSnafu)
794 }
795}
796
797pub struct InstanceCreator {
800 metadata_kv_backend_creator: Box<dyn MetadataKvBackendCreator>,
803 node_manager_creator: Box<dyn NodeManagerCreator>,
804 table_id_allocator_creator: Box<dyn TableIdAllocatorCreator>,
805 procedure_executor_creator: Box<dyn ProcedureExecutorCreator>,
806 leader_services_controller: Box<dyn StandaloneLeaderServicesController>,
807 open_regions_writable_override: Option<bool>,
808}
809
810impl InstanceCreator {
811 pub fn new(
812 node_manager_creator: Box<dyn NodeManagerCreator>,
813 table_id_allocator_creator: Box<dyn TableIdAllocatorCreator>,
814 procedure_executor_creator: Box<dyn ProcedureExecutorCreator>,
815 ) -> Self {
816 Self {
817 metadata_kv_backend_creator: Box::new(DefaultMetadataKvBackendCreator),
818 node_manager_creator,
819 table_id_allocator_creator,
820 procedure_executor_creator,
821 leader_services_controller: Box::new(DefaultStandaloneLeaderServicesController),
822 open_regions_writable_override: None,
823 }
824 }
825
826 pub fn with_metadata_kv_backend_creator(
827 mut self,
828 metadata_kv_backend_creator: Box<dyn MetadataKvBackendCreator>,
829 ) -> Self {
830 self.metadata_kv_backend_creator = metadata_kv_backend_creator;
831 self
832 }
833
834 pub fn map_metadata_kv_backend_creator<F>(mut self, f: F) -> Self
839 where
840 F: FnOnce(Box<dyn MetadataKvBackendCreator>) -> Box<dyn MetadataKvBackendCreator>,
841 {
842 self.metadata_kv_backend_creator = f(self.metadata_kv_backend_creator);
843 self
844 }
845
846 pub fn map_node_manager_creator<F>(mut self, f: F) -> Self
848 where
849 F: FnOnce(Box<dyn NodeManagerCreator>) -> Box<dyn NodeManagerCreator>,
850 {
851 self.node_manager_creator = f(self.node_manager_creator);
852 self
853 }
854
855 pub fn map_procedure_executor_creator<F>(mut self, f: F) -> Self
857 where
858 F: FnOnce(Box<dyn ProcedureExecutorCreator>) -> Box<dyn ProcedureExecutorCreator>,
859 {
860 self.procedure_executor_creator = f(self.procedure_executor_creator);
861 self
862 }
863
864 pub fn with_leader_services_controller(
866 mut self,
867 leader_services_controller: Box<dyn StandaloneLeaderServicesController>,
868 ) -> Self {
869 self.leader_services_controller = leader_services_controller;
870 self
871 }
872
873 pub fn with_open_regions_writable_override(mut self, writable: bool) -> Self {
881 self.open_regions_writable_override = Some(writable);
882 self
883 }
884}
885
886impl Default for InstanceCreator {
887 fn default() -> Self {
888 Self {
889 metadata_kv_backend_creator: Box::new(DefaultMetadataKvBackendCreator),
890 node_manager_creator: Box::new(DefaultNodeManagerCreator),
891 table_id_allocator_creator: Box::new(DefaultTableIdAllocatorCreator),
892 procedure_executor_creator: Box::new(DefaultProcedureExecutorCreator),
893 leader_services_controller: Box::new(DefaultStandaloneLeaderServicesController),
894 open_regions_writable_override: None,
895 }
896 }
897}
898
899pub struct InstanceCreatorResult {
902 pub kv_backend: KvBackendRef,
903 pub node_manager: NodeManagerRef,
904 pub table_id_allocator: Arc<Sequence>,
905}
906
907#[cfg(test)]
908mod tests {
909 use std::default::Default;
910 use std::io::Write;
911 use std::time::Duration;
912
913 use auth::{Identity, Password, UserProviderRef};
914 use clap::{CommandFactory, Parser};
915 use common_base::readable_size::ReadableSize;
916 use common_config::ENV_VAR_SEP;
917 use common_options::plugin_options::StandaloneFlag;
918 use common_test_util::temp_dir::create_named_temp_file;
919 use common_wal::config::DatanodeWalConfig;
920 use frontend::frontend::FrontendOptions;
921 use object_store::config::{FileConfig, GcsConfig};
922 use servers::grpc::GrpcOptions;
923
924 use super::*;
925 use crate::options::GlobalOptions;
926
927 #[tokio::test]
928 async fn test_try_from_start_command_to_anymap() {
929 let fe_opts = FrontendOptions {
930 user_provider: Some("static_user_provider:cmd:test=test".to_string()),
931 ..Default::default()
932 };
933
934 let mut plugins = Plugins::new();
935 plugins.insert(StandaloneFlag);
936 plugins::setup_frontend_plugins(&mut plugins, &[], &fe_opts)
937 .await
938 .unwrap();
939
940 let provider = plugins.get::<UserProviderRef>().unwrap();
941 let result = provider
942 .authenticate(
943 Identity::UserId("test", None),
944 Password::PlainText("test".to_string().into()),
945 )
946 .await;
947 let _ = result.unwrap();
948 }
949
950 #[test]
951 fn test_toml() {
952 let opts = StandaloneOptions::default();
953 let toml_string = toml::to_string(&opts).unwrap();
954 let _parsed: StandaloneOptions = toml::from_str(&toml_string).unwrap();
955 }
956
957 #[test]
958 fn test_read_from_config_file() {
959 let mut file = create_named_temp_file();
960 let toml_str = r#"
961 enable_memory_catalog = true
962
963 [wal]
964 provider = "raft_engine"
965 dir = "./greptimedb_data/test/wal"
966 file_size = "1GB"
967 purge_threshold = "50GB"
968 purge_interval = "10m"
969 read_batch_size = 128
970 sync_write = false
971
972 [storage]
973 data_home = "./greptimedb_data/"
974 type = "File"
975
976 [[storage.providers]]
977 type = "Gcs"
978 bucket = "foo"
979 endpoint = "bar"
980
981 [[storage.providers]]
982 type = "S3"
983 access_key_id = "access_key_id"
984 secret_access_key = "secret_access_key"
985
986 [storage.compaction]
987 max_inflight_tasks = 3
988 max_files_in_level0 = 7
989 max_purge_tasks = 32
990
991 [storage.manifest]
992 checkpoint_margin = 9
993 gc_duration = '7s'
994
995 [http]
996 addr = "127.0.0.1:4000"
997 timeout = "33s"
998 body_limit = "128MB"
999
1000 [opentsdb]
1001 enable = true
1002
1003 [logging]
1004 level = "debug"
1005 dir = "./greptimedb_data/test/logs"
1006 "#;
1007 write!(file, "{}", toml_str).unwrap();
1008 let cmd = StartCommand {
1009 config_file: Some(file.path().to_str().unwrap().to_string()),
1010 user_provider: Some("static_user_provider:cmd:test=test".to_string()),
1011 ..Default::default()
1012 };
1013
1014 let options = cmd
1015 .load_options(&GlobalOptions::default())
1016 .unwrap()
1017 .component;
1018 let fe_opts = options.frontend_options();
1019 let dn_opts = options.datanode_options();
1020 let logging_opts = options.logging;
1021 assert_eq!("127.0.0.1:4000".to_string(), fe_opts.http.addr);
1022 assert_eq!(Duration::from_secs(33), fe_opts.http.timeout);
1023 assert_eq!(ReadableSize::mb(128), fe_opts.http.body_limit);
1024 assert_eq!("127.0.0.1:4001".to_string(), fe_opts.grpc.bind_addr);
1025 assert!(fe_opts.mysql.enable);
1026 assert_eq!("127.0.0.1:4002", fe_opts.mysql.addr);
1027 assert_eq!(2, fe_opts.mysql.runtime_size);
1028 assert_eq!(None, fe_opts.mysql.reject_no_database);
1029 assert!(fe_opts.influxdb.enable);
1030 assert!(fe_opts.opentsdb.enable);
1031
1032 let DatanodeWalConfig::RaftEngine(raft_engine_config) = dn_opts.wal else {
1033 unreachable!()
1034 };
1035 assert_eq!(
1036 "./greptimedb_data/test/wal",
1037 raft_engine_config.dir.unwrap()
1038 );
1039
1040 assert!(matches!(
1041 &dn_opts.storage.store,
1042 object_store::config::ObjectStoreConfig::File(FileConfig { .. })
1043 ));
1044 assert_eq!(dn_opts.storage.providers.len(), 2);
1045 assert!(matches!(
1046 dn_opts.storage.providers[0],
1047 object_store::config::ObjectStoreConfig::Gcs(GcsConfig { .. })
1048 ));
1049 match &dn_opts.storage.providers[1] {
1050 object_store::config::ObjectStoreConfig::S3(s3_config) => {
1051 assert_eq!(
1052 "SecretBox<alloc::string::String>([REDACTED])".to_string(),
1053 format!("{:?}", s3_config.connection.access_key_id)
1054 );
1055 }
1056 _ => {
1057 unreachable!()
1058 }
1059 }
1060
1061 assert_eq!("debug", logging_opts.level.as_ref().unwrap());
1062 assert_eq!("./greptimedb_data/test/logs".to_string(), logging_opts.dir);
1063 }
1064
1065 #[test]
1066 fn test_load_log_options_from_cli() {
1067 let cmd = StartCommand {
1068 user_provider: Some("static_user_provider:cmd:test=test".to_string()),
1069 mysql_addr: Some("127.0.0.1:4002".to_string()),
1070 postgres_addr: Some("127.0.0.1:4003".to_string()),
1071 ..Default::default()
1072 };
1073
1074 let opts = cmd
1075 .load_options(&GlobalOptions {
1076 log_dir: Some("./greptimedb_data/test/logs".to_string()),
1077 log_level: Some("debug".to_string()),
1078
1079 #[cfg(feature = "tokio-console")]
1080 tokio_console_addr: None,
1081 })
1082 .unwrap()
1083 .component;
1084
1085 assert_eq!("./greptimedb_data/test/logs", opts.logging.dir);
1086 assert_eq!("debug", opts.logging.level.unwrap());
1087 }
1088
1089 #[test]
1090 fn test_config_precedence_order() {
1091 let mut file = create_named_temp_file();
1092 let toml_str = r#"
1093 [http]
1094 addr = "127.0.0.1:4000"
1095
1096 [logging]
1097 level = "debug"
1098 "#;
1099 write!(file, "{}", toml_str).unwrap();
1100
1101 let env_prefix = "STANDALONE_UT";
1102 temp_env::with_vars(
1103 [
1104 (
1105 [
1107 env_prefix.to_string(),
1108 "logging".to_uppercase(),
1109 "dir".to_uppercase(),
1110 ]
1111 .join(ENV_VAR_SEP),
1112 Some("/other/log/dir"),
1113 ),
1114 (
1115 [
1117 env_prefix.to_string(),
1118 "logging".to_uppercase(),
1119 "level".to_uppercase(),
1120 ]
1121 .join(ENV_VAR_SEP),
1122 Some("info"),
1123 ),
1124 (
1125 [
1127 env_prefix.to_string(),
1128 "http".to_uppercase(),
1129 "addr".to_uppercase(),
1130 ]
1131 .join(ENV_VAR_SEP),
1132 Some("127.0.0.1:24000"),
1133 ),
1134 ],
1135 || {
1136 let command = StartCommand {
1137 config_file: Some(file.path().to_str().unwrap().to_string()),
1138 http_addr: Some("127.0.0.1:14000".to_string()),
1139 env_prefix: env_prefix.to_string(),
1140 ..Default::default()
1141 };
1142
1143 let opts = command.load_options(&Default::default()).unwrap().component;
1144
1145 assert_eq!(opts.logging.dir, "/other/log/dir");
1147
1148 assert_eq!(opts.logging.level.as_ref().unwrap(), "debug");
1150
1151 let fe_opts = opts.frontend_options();
1153 assert_eq!(fe_opts.http.addr, "127.0.0.1:14000");
1154 assert_eq!(ReadableSize::mb(64), fe_opts.http.body_limit);
1155
1156 assert_eq!(fe_opts.grpc.bind_addr, GrpcOptions::default().bind_addr);
1158 },
1159 );
1160 }
1161
1162 #[test]
1163 fn test_parse_grpc_bind_addr_aliases() {
1164 let command =
1165 StartCommand::try_parse_from(["standalone", "--grpc-bind-addr", "127.0.0.1:14001"])
1166 .unwrap();
1167 assert_eq!(command.grpc_bind_addr.as_deref(), Some("127.0.0.1:14001"));
1168
1169 let command =
1170 StartCommand::try_parse_from(["standalone", "--rpc-bind-addr", "127.0.0.1:24001"])
1171 .unwrap();
1172 assert_eq!(command.grpc_bind_addr.as_deref(), Some("127.0.0.1:24001"));
1173
1174 let command =
1175 StartCommand::try_parse_from(["standalone", "--rpc-addr", "127.0.0.1:34001"]).unwrap();
1176 assert_eq!(command.grpc_bind_addr.as_deref(), Some("127.0.0.1:34001"));
1177 }
1178
1179 #[test]
1180 fn test_help_uses_grpc_option_names() {
1181 let mut cmd = StartCommand::command();
1182 let mut help = Vec::new();
1183 cmd.write_long_help(&mut help).unwrap();
1184 let help = String::from_utf8(help).unwrap();
1185
1186 assert!(help.contains("--grpc-bind-addr"));
1187 assert!(!help.contains("--rpc-bind-addr"));
1188 assert!(!help.contains("--rpc-addr"));
1189 }
1190
1191 #[test]
1192 fn test_load_default_standalone_options() {
1193 let options =
1194 StandaloneOptions::load_layered_options(None, "GREPTIMEDB_STANDALONE").unwrap();
1195 let default_options = StandaloneOptions::default();
1196 assert_eq!(options.enable_telemetry, default_options.enable_telemetry);
1197 assert_eq!(options.http, default_options.http);
1198 assert_eq!(options.grpc, default_options.grpc);
1199 assert_eq!(options.mysql, default_options.mysql);
1200 assert_eq!(options.postgres, default_options.postgres);
1201 assert_eq!(options.opentsdb, default_options.opentsdb);
1202 assert_eq!(options.influxdb, default_options.influxdb);
1203 assert_eq!(options.prom_store, default_options.prom_store);
1204 assert_eq!(options.wal, default_options.wal);
1205 assert_eq!(options.metadata_store, default_options.metadata_store);
1206 assert_eq!(options.procedure, default_options.procedure);
1207 assert_eq!(options.logging, default_options.logging);
1208 assert_eq!(options.region_engine, default_options.region_engine);
1209 }
1210
1211 #[test]
1212 fn test_cache_config() {
1213 let toml_str = r#"
1214 [storage]
1215 data_home = "test_data_home"
1216 type = "S3"
1217 [storage.cache_config]
1218 enable_read_cache = true
1219 "#;
1220 let mut opts: StandaloneOptions = toml::from_str(toml_str).unwrap();
1221 opts.sanitize();
1222 assert!(opts.storage.store.cache_config().unwrap().enable_read_cache);
1223 assert_eq!(
1224 opts.storage.store.cache_config().unwrap().cache_path,
1225 "test_data_home"
1226 );
1227 }
1228}