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