1use std::path::Path;
18use std::sync::Arc;
19use std::time::{Duration, Instant};
20
21use common_base::Plugins;
22use common_error::ext::BoxedError;
23use common_greptimedb_telemetry::GreptimeDBTelemetryTask;
24use common_meta::cache::{LayeredCacheRegistry, SchemaCacheRef, TableSchemaCacheRef};
25use common_meta::cache_invalidator::CacheInvalidatorRef;
26use common_meta::datanode::TopicStatsReporter;
27use common_meta::key::runtime_switch::RuntimeSwitchManager;
28use common_meta::key::{SchemaMetadataManager, SchemaMetadataManagerRef};
29use common_meta::kv_backend::KvBackendRef;
30pub use common_procedure::options::ProcedureConfig;
31use common_query::prelude::set_default_prefix;
32use common_stat::ResourceStatImpl;
33use common_telemetry::{error, info, warn};
34use common_wal::config::DatanodeWalConfig;
35use common_wal::config::kafka::DatanodeKafkaConfig;
36use common_wal::config::raft_engine::RaftEngineConfig;
37use file_engine::engine::FileRegionEngine;
38use log_store::kafka::log_store::KafkaLogStore;
39use log_store::kafka::{GlobalIndexCollector, default_index_file};
40use log_store::noop::log_store::NoopLogStore;
41use log_store::raft_engine::log_store::RaftEngineLogStore;
42use meta_client::MetaClientRef;
43use metric_engine::engine::MetricEngine;
44use mito2::config::MitoConfig;
45use mito2::engine::{MitoEngine, MitoEngineBuilder};
46use mito2::region::opener::PartitionExprFetcherRef;
47use mito2::sst::file_ref::{FileReferenceManager, FileReferenceManagerRef};
48use object_store::manager::{ObjectStoreManager, ObjectStoreManagerRef};
49use object_store::util::normalize_dir;
50use query::QueryEngineFactory;
51use query::dummy_catalog::{DummyCatalogManager, TableProviderFactoryRef};
52use servers::server::ServerHandlers;
53use snafu::{OptionExt, ResultExt, ensure};
54use store_api::path_utils::WAL_DIR;
55use store_api::region_engine::{
56 RegionEngineRef, RegionRole, SetRegionRoleStateResponse, SettableRegionRoleState,
57};
58use tokio::fs;
59use tokio::sync::Notify;
60
61use crate::config::{DatanodeOptions, RegionEngineConfig, StorageConfig};
62use crate::error::{
63 self, BuildDatanodeSnafu, BuildMetricEngineSnafu, BuildMitoEngineSnafu, CreateDirSnafu,
64 DataFusionSnafu, GetMetadataSnafu, MissingCacheSnafu, MissingNodeIdSnafu, OpenLogStoreSnafu,
65 Result, ShutdownInstanceSnafu, ShutdownServerSnafu, StartServerSnafu,
66};
67use crate::event_listener::{
68 NoopRegionServerEventListener, RegionServerEventListenerRef, RegionServerEventReceiver,
69 new_region_server_event_channel,
70};
71use crate::greptimedb_telemetry::get_greptimedb_telemetry_task;
72use crate::heartbeat::HeartbeatTask;
73use crate::partition_expr_fetcher::MetaPartitionExprFetcher;
74use crate::region_server::{DummyTableProviderFactory, RegionServer};
75use crate::store::{self, new_object_store_without_cache};
76use crate::utils::{RegionOpenRequests, build_region_open_requests};
77
78pub struct Datanode {
80 services: ServerHandlers,
81 heartbeat_task: Option<HeartbeatTask>,
82 region_event_receiver: Option<RegionServerEventReceiver>,
83 region_server: RegionServer,
84 greptimedb_telemetry_task: Arc<GreptimeDBTelemetryTask>,
85 leases_notifier: Option<Arc<Notify>>,
86 plugins: Plugins,
87}
88
89impl Datanode {
90 pub async fn start(&mut self) -> Result<()> {
91 info!("Starting datanode instance...");
92
93 self.start_heartbeat().await?;
94 self.wait_coordinated().await;
95
96 self.start_telemetry();
97
98 self.services.start_all().await.context(StartServerSnafu)
99 }
100
101 pub fn server_handlers(&self) -> &ServerHandlers {
102 &self.services
103 }
104
105 pub fn start_telemetry(&self) {
106 if let Err(e) = self.greptimedb_telemetry_task.start() {
107 warn!(e; "Failed to start telemetry task!");
108 }
109 }
110
111 pub async fn start_heartbeat(&mut self) -> Result<()> {
112 if let Some(task) = &self.heartbeat_task {
113 let receiver = self.region_event_receiver.take().unwrap();
115
116 task.start(receiver, self.leases_notifier.clone()).await?;
117 }
118 Ok(())
119 }
120
121 pub async fn wait_coordinated(&mut self) {
123 if let Some(notifier) = self.leases_notifier.take() {
124 notifier.notified().await;
125 }
126 }
127
128 pub fn setup_services(&mut self, services: ServerHandlers) {
129 self.services = services;
130 }
131
132 pub async fn shutdown(&mut self) -> Result<()> {
133 self.services
134 .shutdown_all()
135 .await
136 .context(ShutdownServerSnafu)?;
137
138 let _ = self.greptimedb_telemetry_task.stop().await;
139 if let Some(heartbeat_task) = &self.heartbeat_task {
140 heartbeat_task
141 .close()
142 .map_err(BoxedError::new)
143 .context(ShutdownInstanceSnafu)?;
144 }
145 self.region_server.stop().await?;
146 Ok(())
147 }
148
149 pub fn region_server(&self) -> RegionServer {
150 self.region_server.clone()
151 }
152
153 pub fn plugins(&self) -> Plugins {
154 self.plugins.clone()
155 }
156}
157
158pub struct DatanodeBuilder {
159 opts: DatanodeOptions,
160 table_provider_factory: Option<TableProviderFactoryRef>,
161 plugins: Plugins,
162 meta_client: Option<MetaClientRef>,
163 kv_backend: KvBackendRef,
164 cache_registry: Option<Arc<LayeredCacheRegistry>>,
165 topic_stats_reporter: Option<Box<dyn TopicStatsReporter>>,
166 open_regions_writable_override: Option<bool>,
167 #[cfg(feature = "enterprise")]
168 extension_range_provider_factory: Option<mito2::extension::BoxedExtensionRangeProviderFactory>,
169}
170
171impl DatanodeBuilder {
172 pub fn new(opts: DatanodeOptions, plugins: Plugins, kv_backend: KvBackendRef) -> Self {
173 Self {
174 opts,
175 table_provider_factory: None,
176 plugins,
177 meta_client: None,
178 kv_backend,
179 cache_registry: None,
180 open_regions_writable_override: None,
181 #[cfg(feature = "enterprise")]
182 extension_range_provider_factory: None,
183 topic_stats_reporter: None,
184 }
185 }
186
187 pub fn options(&self) -> &DatanodeOptions {
188 &self.opts
189 }
190
191 pub fn with_meta_client(&mut self, client: MetaClientRef) -> &mut Self {
192 self.meta_client = Some(client);
193 self
194 }
195
196 pub fn with_cache_registry(&mut self, registry: Arc<LayeredCacheRegistry>) -> &mut Self {
197 self.cache_registry = Some(registry);
198 self
199 }
200
201 pub fn kv_backend(&self) -> &KvBackendRef {
202 &self.kv_backend
203 }
204
205 pub fn meta_client(&self) -> Option<&MetaClientRef> {
206 self.meta_client.as_ref()
207 }
208
209 pub fn cache_registry(&self) -> Option<&Arc<LayeredCacheRegistry>> {
210 self.cache_registry.as_ref()
211 }
212
213 pub fn set_plugins(&mut self, plugins: Plugins) {
214 self.plugins = plugins;
215 }
216
217 pub fn with_table_provider_factory(&mut self, factory: TableProviderFactoryRef) -> &mut Self {
218 self.table_provider_factory = Some(factory);
219 self
220 }
221
222 pub fn with_open_regions_writable_override(&mut self, writable: bool) -> &mut Self {
232 self.open_regions_writable_override = Some(writable);
233 self
234 }
235
236 #[cfg(feature = "enterprise")]
237 pub fn with_extension_range_provider(
238 &mut self,
239 extension_range_provider_factory: mito2::extension::BoxedExtensionRangeProviderFactory,
240 ) -> &mut Self {
241 self.extension_range_provider_factory = Some(extension_range_provider_factory);
242 self
243 }
244
245 pub async fn build(mut self) -> Result<Datanode> {
246 let node_id = self.opts.node_id.context(MissingNodeIdSnafu)?;
247 set_default_prefix(self.opts.default_column_prefix.as_deref())
248 .map_err(BoxedError::new)
249 .context(BuildDatanodeSnafu)?;
250
251 let meta_client = self.meta_client.take();
252
253 let controlled_by_metasrv = meta_client.is_some();
257
258 let (region_event_listener, region_event_receiver) = if controlled_by_metasrv {
260 let (tx, rx) = new_region_server_event_channel();
261 (Box::new(tx) as _, Some(rx))
262 } else {
263 (Box::new(NoopRegionServerEventListener) as _, None)
264 };
265
266 let cache_registry = self.cache_registry.take().context(MissingCacheSnafu)?;
267 let schema_cache: SchemaCacheRef = cache_registry.get().context(MissingCacheSnafu)?;
268 let table_id_schema_cache: TableSchemaCacheRef =
269 cache_registry.get().context(MissingCacheSnafu)?;
270
271 let schema_metadata_manager = Arc::new(SchemaMetadataManager::new(
272 table_id_schema_cache,
273 schema_cache,
274 ));
275
276 let gc_enabled = self.opts.region_engine.iter().any(|engine| {
277 if let RegionEngineConfig::Mito(config) = engine {
278 config.gc.enable
279 } else {
280 false
281 }
282 });
283
284 let file_ref_manager = Arc::new(FileReferenceManager::with_gc_enabled(
285 Some(node_id),
286 gc_enabled,
287 ));
288 let region_server = self
289 .new_region_server(
290 schema_metadata_manager,
291 region_event_listener,
292 file_ref_manager,
293 )
294 .await?;
295
296 let runtime_switch_manager = RuntimeSwitchManager::new(self.kv_backend.clone());
298 let is_recovery_mode = runtime_switch_manager
299 .recovery_mode()
300 .await
301 .context(GetMetadataSnafu)?;
302
303 let region_open_requests =
304 build_region_open_requests(node_id, self.kv_backend.clone()).await?;
305 let open_with_writable = self
306 .open_regions_writable_override
307 .unwrap_or(!controlled_by_metasrv);
308 let open_all_regions = open_all_regions(
309 region_server.clone(),
310 region_open_requests,
311 open_with_writable,
312 self.opts.init_regions_parallelism,
313 is_recovery_mode,
315 );
316
317 if self.opts.init_regions_in_background {
318 common_runtime::spawn_global(async move {
320 if let Err(err) = open_all_regions.await {
321 error!(err; "Failed to open regions during the startup.");
322 }
323 });
324 } else {
325 open_all_regions.await?;
326 }
327
328 let heartbeat_task = if let Some(meta_client) = meta_client {
329 let task = self
330 .create_heartbeat_task(®ion_server, meta_client, cache_registry)
331 .await?;
332 Some(task)
333 } else {
334 None
335 };
336
337 let is_standalone = heartbeat_task.is_none();
338 let greptimedb_telemetry_task = get_greptimedb_telemetry_task(
339 Some(self.opts.storage.data_home.clone()),
340 is_standalone && self.opts.enable_telemetry,
341 )
342 .await;
343
344 let leases_notifier = if self.opts.require_lease_before_startup && !is_standalone {
345 Some(Arc::new(Notify::new()))
346 } else {
347 None
348 };
349
350 Ok(Datanode {
351 services: ServerHandlers::default(),
352 heartbeat_task,
353 region_server,
354 greptimedb_telemetry_task,
355 region_event_receiver,
356 leases_notifier,
357 plugins: self.plugins.clone(),
358 })
359 }
360
361 async fn create_heartbeat_task(
362 &self,
363 region_server: &RegionServer,
364 meta_client: MetaClientRef,
365 cache_invalidator: CacheInvalidatorRef,
366 ) -> Result<HeartbeatTask> {
367 let stat = {
368 let mut stat = ResourceStatImpl::default();
369 stat.start_collect_cpu_usage();
370 Arc::new(stat)
371 };
372
373 HeartbeatTask::try_new(
374 &self.opts,
375 region_server.clone(),
376 meta_client,
377 self.kv_backend.clone(),
378 cache_invalidator,
379 self.plugins.clone(),
380 stat,
381 )
382 .await
383 }
384
385 pub async fn build_object_store_manager(cfg: &StorageConfig) -> Result<ObjectStoreManagerRef> {
387 let object_store = store::new_object_store(cfg.store.clone(), &cfg.data_home).await?;
388 let default_name = cfg.store.config_name();
389 let mut object_store_manager = ObjectStoreManager::new(default_name, object_store);
390 for store in &cfg.providers {
391 object_store_manager.add(
392 store.config_name(),
393 store::new_object_store(store.clone(), &cfg.data_home).await?,
394 );
395 }
396 Ok(Arc::new(object_store_manager))
397 }
398
399 #[cfg(test)]
400 async fn initialize_region_server(
402 &self,
403 region_server: &RegionServer,
404 open_with_writable: bool,
405 ) -> Result<()> {
406 let node_id = self.opts.node_id.context(MissingNodeIdSnafu)?;
407
408 let runtime_switch_manager = RuntimeSwitchManager::new(self.kv_backend.clone());
410 let is_recovery_mode = runtime_switch_manager
411 .recovery_mode()
412 .await
413 .context(GetMetadataSnafu)?;
414 let region_open_requests =
415 build_region_open_requests(node_id, self.kv_backend.clone()).await?;
416
417 open_all_regions(
418 region_server.clone(),
419 region_open_requests,
420 open_with_writable,
421 self.opts.init_regions_parallelism,
422 is_recovery_mode,
423 )
424 .await
425 }
426
427 async fn new_region_server(
428 &mut self,
429 schema_metadata_manager: SchemaMetadataManagerRef,
430 event_listener: RegionServerEventListenerRef,
431 file_ref_manager: FileReferenceManagerRef,
432 ) -> Result<RegionServer> {
433 let opts: &DatanodeOptions = &self.opts;
434
435 let query_engine_factory = QueryEngineFactory::try_new_with_plugins(
436 DummyCatalogManager::arc(),
438 None,
439 None,
440 None,
441 None,
442 None,
443 false,
444 self.plugins.clone(),
445 opts.query.clone(),
446 )
447 .context(DataFusionSnafu)?;
448 let query_engine = query_engine_factory.query_engine();
449
450 let table_provider_factory = self
451 .table_provider_factory
452 .clone()
453 .unwrap_or_else(|| Arc::new(DummyTableProviderFactory));
454 let mut region_server = RegionServer::with_table_provider(
455 query_engine,
456 common_runtime::global_runtime(),
457 event_listener,
458 table_provider_factory,
459 opts.max_concurrent_queries,
460 opts.concurrent_query_limiter_timeout,
461 opts.grpc.flight_compression,
462 );
463 region_server.install_remote_dyn_filter_receiver_injector(&self.plugins);
464
465 let object_store_manager = Self::build_object_store_manager(&opts.storage).await?;
466 let engines = self
467 .build_store_engines(
468 object_store_manager,
469 schema_metadata_manager,
470 file_ref_manager,
471 self.plugins.clone(),
472 )
473 .await?;
474 for engine in engines {
475 region_server.register_engine(engine);
476 }
477 if let Some(topic_stats_reporter) = self.topic_stats_reporter.take() {
478 region_server.set_topic_stats_reporter(topic_stats_reporter);
479 }
480
481 Ok(region_server)
482 }
483
484 async fn build_store_engines(
488 &mut self,
489 object_store_manager: ObjectStoreManagerRef,
490 schema_metadata_manager: SchemaMetadataManagerRef,
491 file_ref_manager: FileReferenceManagerRef,
492 plugins: Plugins,
493 ) -> Result<Vec<RegionEngineRef>> {
494 let mut metric_engine_config = metric_engine::config::EngineConfig::default();
495 let mut mito_engine_config = MitoConfig::default();
496 let mut file_engine_config = file_engine::config::EngineConfig::default();
497
498 for engine in &self.opts.region_engine {
499 match engine {
500 RegionEngineConfig::Mito(config) => {
501 mito_engine_config = config.clone();
502 }
503 RegionEngineConfig::File(config) => {
504 file_engine_config = config.clone();
505 }
506 RegionEngineConfig::Metric(metric_config) => {
507 metric_engine_config = metric_config.clone();
508 }
509 }
510 }
511
512 let fetcher = Arc::new(MetaPartitionExprFetcher::new(self.kv_backend.clone()));
514 let mito_engine = self
515 .build_mito_engine(
516 object_store_manager.clone(),
517 mito_engine_config,
518 schema_metadata_manager.clone(),
519 file_ref_manager.clone(),
520 fetcher.clone(),
521 plugins.clone(),
522 )
523 .await?;
524
525 let metric_engine = MetricEngine::try_new(mito_engine.clone(), metric_engine_config)
526 .context(BuildMetricEngineSnafu)?;
527
528 let file_engine = FileRegionEngine::new(
529 file_engine_config,
530 object_store_manager.default_object_store().clone(), );
532
533 Ok(vec![
534 Arc::new(mito_engine) as _,
535 Arc::new(metric_engine) as _,
536 Arc::new(file_engine) as _,
537 ])
538 }
539
540 async fn build_mito_engine(
542 &mut self,
543 object_store_manager: ObjectStoreManagerRef,
544 mut config: MitoConfig,
545 schema_metadata_manager: SchemaMetadataManagerRef,
546 file_ref_manager: FileReferenceManagerRef,
547 partition_expr_fetcher: PartitionExprFetcherRef,
548 plugins: Plugins,
549 ) -> Result<MitoEngine> {
550 let opts = &self.opts;
551 if opts.storage.is_object_storage() {
552 config.enable_write_cache = true;
554 info!("Configured 'enable_write_cache=true' for mito engine.");
555 }
556
557 let mito_engine = match &opts.wal {
558 DatanodeWalConfig::RaftEngine(raft_engine_config) => {
559 let log_store =
560 Self::build_raft_engine_log_store(&opts.storage.data_home, raft_engine_config)
561 .await?;
562
563 let builder = MitoEngineBuilder::new(
564 &opts.storage.data_home,
565 config,
566 log_store,
567 object_store_manager,
568 schema_metadata_manager,
569 file_ref_manager,
570 partition_expr_fetcher.clone(),
571 plugins,
572 );
573
574 #[cfg(feature = "enterprise")]
575 let builder = builder.with_extension_range_provider_factory(
576 self.extension_range_provider_factory.take(),
577 );
578
579 builder.try_build().await.context(BuildMitoEngineSnafu)?
580 }
581 DatanodeWalConfig::Kafka(kafka_config) => {
582 if kafka_config.create_index && opts.node_id.is_none() {
583 warn!("The WAL index creation only available in distributed mode.")
584 }
585 let global_index_collector = if kafka_config.create_index
586 && let Some(node_id) = opts.node_id
587 {
588 let operator = new_object_store_without_cache(
589 &opts.storage.store,
590 &opts.storage.data_home,
591 )
592 .await?;
593 let path = default_index_file(node_id);
594 Some(Self::build_global_index_collector(
595 kafka_config.dump_index_interval,
596 operator,
597 path,
598 ))
599 } else {
600 None
601 };
602
603 let log_store =
604 Self::build_kafka_log_store(kafka_config, global_index_collector).await?;
605 self.topic_stats_reporter = Some(log_store.topic_stats_reporter());
606 let builder = MitoEngineBuilder::new(
607 &opts.storage.data_home,
608 config,
609 log_store,
610 object_store_manager,
611 schema_metadata_manager,
612 file_ref_manager,
613 partition_expr_fetcher,
614 plugins,
615 );
616
617 #[cfg(feature = "enterprise")]
618 let builder = builder.with_extension_range_provider_factory(
619 self.extension_range_provider_factory.take(),
620 );
621
622 builder.try_build().await.context(BuildMitoEngineSnafu)?
623 }
624 DatanodeWalConfig::Noop => {
625 let log_store = Arc::new(NoopLogStore);
626
627 let builder = MitoEngineBuilder::new(
628 &opts.storage.data_home,
629 config,
630 log_store,
631 object_store_manager,
632 schema_metadata_manager,
633 file_ref_manager,
634 partition_expr_fetcher.clone(),
635 plugins,
636 );
637
638 #[cfg(feature = "enterprise")]
639 let builder = builder.with_extension_range_provider_factory(
640 self.extension_range_provider_factory.take(),
641 );
642
643 builder.try_build().await.context(BuildMitoEngineSnafu)?
644 }
645 };
646 Ok(mito_engine)
647 }
648
649 async fn build_raft_engine_log_store(
651 data_home: &str,
652 config: &RaftEngineConfig,
653 ) -> Result<Arc<RaftEngineLogStore>> {
654 let data_home = normalize_dir(data_home);
655 let wal_dir = match &config.dir {
656 Some(dir) => dir.clone(),
657 None => format!("{}{WAL_DIR}", data_home),
658 };
659
660 fs::create_dir_all(Path::new(&wal_dir))
662 .await
663 .context(CreateDirSnafu { dir: &wal_dir })?;
664 info!(
665 "Creating raft-engine logstore with config: {:?} and storage path: {}",
666 config, &wal_dir
667 );
668 let logstore = RaftEngineLogStore::try_new(wal_dir, config)
669 .await
670 .map_err(Box::new)
671 .context(OpenLogStoreSnafu)?;
672
673 Ok(Arc::new(logstore))
674 }
675
676 async fn build_kafka_log_store(
678 config: &DatanodeKafkaConfig,
679 global_index_collector: Option<GlobalIndexCollector>,
680 ) -> Result<Arc<KafkaLogStore>> {
681 KafkaLogStore::try_new(config, global_index_collector)
682 .await
683 .map_err(Box::new)
684 .context(OpenLogStoreSnafu)
685 .map(Arc::new)
686 }
687
688 fn build_global_index_collector(
690 dump_index_interval: Duration,
691 operator: object_store::ObjectStore,
692 path: String,
693 ) -> GlobalIndexCollector {
694 GlobalIndexCollector::new(dump_index_interval, operator, path)
695 }
696}
697
698async fn open_all_regions(
700 region_server: RegionServer,
701 region_open_requests: RegionOpenRequests,
702 open_with_writable: bool,
703 init_regions_parallelism: usize,
704 ignore_nonexistent_region: bool,
705) -> Result<()> {
706 let RegionOpenRequests {
707 leader_regions,
708 #[cfg(feature = "enterprise")]
709 follower_regions,
710 } = region_open_requests;
711
712 let leader_region_num = leader_regions.len();
713 info!("going to open {} region(s)", leader_region_num);
714 let now = Instant::now();
715 let open_regions = region_server
716 .handle_batch_open_requests(
717 init_regions_parallelism,
718 leader_regions,
719 ignore_nonexistent_region,
720 )
721 .await?;
722 info!(
723 "Opened {} regions in {:?}",
724 open_regions.len(),
725 now.elapsed()
726 );
727 if !ignore_nonexistent_region {
728 ensure!(
729 open_regions.len() == leader_region_num,
730 error::UnexpectedSnafu {
731 violated: format!(
732 "Expected to open {} of regions, only {} of regions has opened",
733 leader_region_num,
734 open_regions.len()
735 )
736 }
737 );
738 } else if open_regions.len() != leader_region_num {
739 warn!(
740 "ignore nonexistent region, expected to open {} of regions, only {} of regions has opened",
741 leader_region_num,
742 open_regions.len()
743 );
744 }
745
746 for region_id in open_regions {
747 if open_with_writable {
748 let res = region_server.set_region_role(region_id, RegionRole::Leader);
749 match res {
750 Ok(_) => {
751 if let SetRegionRoleStateResponse::InvalidTransition(err) = region_server
753 .set_region_role_state_gracefully(
754 region_id,
755 SettableRegionRoleState::Leader,
756 )
757 .await?
758 {
759 error!(err; "failed to convert region {region_id} to leader");
760 }
761 }
762 Err(e) => {
763 error!(e; "failed to convert region {region_id} to leader");
764 }
765 }
766 }
767 }
768
769 #[cfg(feature = "enterprise")]
770 if !follower_regions.is_empty() {
771 use tokio::time::Instant;
772
773 let follower_region_num = follower_regions.len();
774 info!("going to open {} follower region(s)", follower_region_num);
775
776 let now = Instant::now();
777 let open_regions = region_server
778 .handle_batch_open_requests(
779 init_regions_parallelism,
780 follower_regions,
781 ignore_nonexistent_region,
782 )
783 .await?;
784 info!(
785 "Opened {} follower regions in {:?}",
786 open_regions.len(),
787 now.elapsed()
788 );
789
790 if !ignore_nonexistent_region {
791 ensure!(
792 open_regions.len() == follower_region_num,
793 error::UnexpectedSnafu {
794 violated: format!(
795 "Expected to open {} of follower regions, only {} of regions has opened",
796 follower_region_num,
797 open_regions.len()
798 )
799 }
800 );
801 } else if open_regions.len() != follower_region_num {
802 warn!(
803 "ignore nonexistent region, expected to open {} of follower regions, only {} of regions has opened",
804 follower_region_num,
805 open_regions.len()
806 );
807 }
808 }
809
810 info!("all regions are opened");
811
812 Ok(())
813}
814
815#[cfg(test)]
816mod tests {
817 use std::assert_matches;
818 use std::collections::{BTreeMap, HashMap};
819 use std::sync::Arc;
820
821 use cache::build_datanode_cache_registry;
822 use common_base::Plugins;
823 use common_meta::cache::LayeredCacheRegistryBuilder;
824 use common_meta::key::RegionRoleSet;
825 use common_meta::key::datanode_table::DatanodeTableManager;
826 use common_meta::kv_backend::KvBackendRef;
827 use common_meta::kv_backend::memory::MemoryKvBackend;
828 use mito2::engine::MITO_ENGINE_NAME;
829 use store_api::region_request::RegionRequest;
830 use store_api::storage::RegionId;
831
832 use crate::config::DatanodeOptions;
833 use crate::datanode::DatanodeBuilder;
834 use crate::tests::{MockRegionEngine, mock_region_server};
835
836 async fn setup_table_datanode(kv: &KvBackendRef) {
837 let mgr = DatanodeTableManager::new(kv.clone());
838 let txn = mgr
839 .build_create_txn(
840 1028,
841 MITO_ENGINE_NAME,
842 "foo/bar/weny",
843 HashMap::from([("foo".to_string(), "bar".to_string())]),
844 HashMap::default(),
845 BTreeMap::from([(0, RegionRoleSet::new(vec![0, 1, 2], vec![]))]),
846 )
847 .unwrap();
848
849 let r = kv.txn(txn).await.unwrap();
850 assert!(r.succeeded);
851 }
852
853 #[tokio::test]
854 async fn test_initialize_region_server() {
855 common_telemetry::init_default_ut_logging();
856 let mut mock_region_server = mock_region_server();
857 let (mock_region, mut mock_region_handler) = MockRegionEngine::new(MITO_ENGINE_NAME);
858
859 mock_region_server.register_engine(mock_region.clone());
860
861 let kv_backend = Arc::new(MemoryKvBackend::new());
862 let layered_cache_registry = Arc::new(
863 LayeredCacheRegistryBuilder::default()
864 .add_cache_registry(build_datanode_cache_registry(kv_backend.clone()))
865 .build(),
866 );
867
868 let mut builder = DatanodeBuilder::new(
869 DatanodeOptions {
870 node_id: Some(0),
871 ..Default::default()
872 },
873 Plugins::default(),
874 kv_backend.clone(),
875 );
876 builder.with_cache_registry(layered_cache_registry);
877 setup_table_datanode(&(kv_backend as _)).await;
878
879 builder
880 .initialize_region_server(&mock_region_server, false)
881 .await
882 .unwrap();
883
884 for i in 0..3 {
885 let (region_id, req) = mock_region_handler.recv().await.unwrap();
886 assert_eq!(region_id, RegionId::new(1028, i));
887 if let RegionRequest::Open(req) = req {
888 assert_eq!(
889 req.options,
890 HashMap::from([("foo".to_string(), "bar".to_string())])
891 )
892 } else {
893 unreachable!()
894 }
895 }
896
897 assert_matches!(
898 mock_region_handler.try_recv(),
899 Err(tokio::sync::mpsc::error::TryRecvError::Empty)
900 );
901 }
902}