Skip to main content

meta_srv/metasrv/
builder.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::path::Path;
16use std::sync::atomic::AtomicBool;
17use std::sync::{Arc, Mutex, RwLock};
18use std::time::Duration;
19
20use client::client_manager::NodeClients;
21use client::inserter::InsertOptions;
22use common_base::Plugins;
23use common_catalog::consts::{MIN_USER_FLOW_ID, MIN_USER_TABLE_ID};
24use common_event_recorder::{DEFAULT_COMPACTION_TIME_WINDOW, EventRecorderImpl, EventRecorderRef};
25use common_meta::affinity::{AffinityFallback, AffinityRef, NoopAffinity};
26use common_meta::ddl::flow_meta::FlowMetadataAllocator;
27use common_meta::ddl::table_meta::{TableMetadataAllocator, TableMetadataAllocatorRef};
28use common_meta::ddl::{
29    DdlContext, NoopRegionFailureDetectorControl, RegionFailureDetectorControllerRef,
30};
31use common_meta::ddl_manager::{
32    DdlManager, DdlManagerConfiguratorRef, RepartitionProcedureFactoryRef,
33};
34use common_meta::distributed_time_constants::default_distributed_time_constants;
35use common_meta::key::TableMetadataManager;
36use common_meta::key::flow::FlowMetadataManager;
37use common_meta::key::flow::flow_state::FlowStateManager;
38use common_meta::key::runtime_switch::{RuntimeSwitchManager, RuntimeSwitchManagerRef};
39use common_meta::kv_backend::memory::MemoryKvBackend;
40use common_meta::kv_backend::{KvBackendRef, ResettableKvBackendRef};
41use common_meta::node_manager::NodeManagerRef;
42use common_meta::peer::PeerDiscoveryRef;
43use common_meta::reconciliation::manager::ReconciliationManager;
44use common_meta::region_keeper::MemoryRegionKeeper;
45use common_meta::region_registry::LeaderRegionRegistry;
46use common_meta::sequence::SequenceBuilder;
47use common_meta::state_store::KvStateStore;
48use common_meta::stats::topic::TopicStatsRegistry;
49use common_meta::wal_provider::{build_kafka_client, build_wal_provider};
50use common_procedure::ProcedureManagerRef;
51use common_procedure::local::{LocalManager, ManagerConfig};
52use common_stat::ResourceStatImpl;
53use common_telemetry::{info, warn};
54use snafu::{ResultExt, ensure};
55use store_api::storage::MAX_REGION_SEQ;
56
57use crate::bootstrap::build_default_meta_peer_client;
58use crate::cache_invalidator::MetasrvCacheInvalidator;
59use crate::cluster::MetaPeerClientRef;
60use crate::discovery::node_scheduling::SchedulingAwarePeerDiscovery;
61use crate::error::{self, BuildWalProviderSnafu, OtherSnafu, Result};
62use crate::event::EventHandlerImpl;
63use crate::gc::{DefaultGcSchedulerCtx, GcScheduler};
64use crate::greptimedb_telemetry::get_greptimedb_telemetry_task;
65use crate::handler::failure_handler::RegionFailureHandler;
66use crate::handler::flow_state_handler::FlowStateHandler;
67use crate::handler::persist_stats_handler::PersistStatsHandler;
68use crate::handler::region_lease_handler::{CustomizedRegionLeaseRenewerRef, RegionLeaseHandler};
69use crate::handler::{HeartbeatHandlerGroupBuilder, HeartbeatMailbox, Pushers};
70use crate::metasrv::{
71    ElectionRef, FLOW_ID_SEQ, METASRV_DATA_DIR, Metasrv, MetasrvInfo, MetasrvOptions,
72    RegionStatAwareSelectorRef, SelectTarget, SelectorContext, SelectorFactoryRef, SelectorRef,
73    TABLE_ID_SEQ,
74};
75use crate::node_scheduling::NodeSchedulingManager;
76use crate::peer::MetasrvPeerAllocator;
77use crate::procedure::drain_datanode::manager::DrainDatanodeManager;
78use crate::procedure::region_migration::DefaultContextFactory;
79use crate::procedure::region_migration::manager::RegionMigrationManager;
80use crate::procedure::repartition::gc_requirement::RepartitionGcRequirementManager;
81use crate::procedure::repartition::{
82    DefaultRepartitionProcedureFactory, GcDisabledRepartitionProcedureFactory,
83};
84use crate::procedure::wal_prune::Context as WalPruneContext;
85use crate::procedure::wal_prune::manager::{WalPruneManager, WalPruneTicker};
86use crate::region::flush_trigger::RegionFlushTrigger;
87use crate::region::supervisor::{
88    DEFAULT_INITIALIZATION_RETRY_PERIOD, DEFAULT_TICK_INTERVAL, HeartbeatAcceptor,
89    RegionFailureDetectorControl, RegionSupervisor, RegionSupervisorSelector,
90    RegionSupervisorTicker,
91};
92use crate::selector::lease_based::LeaseBasedSelector;
93use crate::selector::round_robin::RoundRobinSelector;
94use crate::service::mailbox::MailboxRef;
95use crate::service::store::cached_kv::LeaderCachedKvBackend;
96use crate::state::State;
97use crate::utils::database::DatabaseOperator;
98use crate::utils::insert_forwarder::InsertForwarder;
99
100/// The time window for twcs compaction of the region stats table.
101const REGION_STATS_TABLE_TWCS_COMPACTION_TIME_WINDOW: Duration = Duration::from_secs(86400);
102
103// TODO(fys): try use derive_builder macro
104pub struct MetasrvBuilder {
105    options: Option<MetasrvOptions>,
106    kv_backend: Option<KvBackendRef>,
107    in_memory: Option<ResettableKvBackendRef>,
108    selector: Option<SelectorRef>,
109    handler_group_builder: Option<HeartbeatHandlerGroupBuilder>,
110    election: Option<ElectionRef>,
111    meta_peer_client: Option<MetaPeerClientRef>,
112    node_manager: Option<NodeManagerRef>,
113    plugins: Option<Plugins>,
114    table_metadata_allocator: Option<TableMetadataAllocatorRef>,
115}
116
117impl MetasrvBuilder {
118    pub fn new() -> Self {
119        Self {
120            kv_backend: None,
121            in_memory: None,
122            selector: None,
123            handler_group_builder: None,
124            meta_peer_client: None,
125            election: None,
126            options: None,
127            node_manager: None,
128            plugins: None,
129            table_metadata_allocator: None,
130        }
131    }
132
133    pub fn options(mut self, options: MetasrvOptions) -> Self {
134        self.options = Some(options);
135        self
136    }
137
138    pub fn kv_backend(mut self, kv_backend: KvBackendRef) -> Self {
139        self.kv_backend = Some(kv_backend);
140        self
141    }
142
143    pub fn in_memory(mut self, in_memory: ResettableKvBackendRef) -> Self {
144        self.in_memory = Some(in_memory);
145        self
146    }
147
148    pub fn selector(mut self, selector: SelectorRef) -> Self {
149        self.selector = Some(selector);
150        self
151    }
152
153    pub fn heartbeat_handler(
154        mut self,
155        handler_group_builder: HeartbeatHandlerGroupBuilder,
156    ) -> Self {
157        self.handler_group_builder = Some(handler_group_builder);
158        self
159    }
160
161    pub fn meta_peer_client(mut self, meta_peer_client: MetaPeerClientRef) -> Self {
162        self.meta_peer_client = Some(meta_peer_client);
163        self
164    }
165
166    pub fn election(mut self, election: Option<ElectionRef>) -> Self {
167        self.election = election;
168        self
169    }
170
171    pub fn node_manager(mut self, node_manager: NodeManagerRef) -> Self {
172        self.node_manager = Some(node_manager);
173        self
174    }
175
176    pub fn plugins(mut self, plugins: Plugins) -> Self {
177        self.plugins = Some(plugins);
178        self
179    }
180
181    pub fn table_metadata_allocator(
182        mut self,
183        table_metadata_allocator: TableMetadataAllocatorRef,
184    ) -> Self {
185        self.table_metadata_allocator = Some(table_metadata_allocator);
186        self
187    }
188
189    pub fn options_ref(&self) -> Option<&MetasrvOptions> {
190        self.options.as_ref()
191    }
192
193    pub fn kv_backend_ref(&self) -> Option<&KvBackendRef> {
194        self.kv_backend.as_ref()
195    }
196
197    pub fn in_memory_ref(&self) -> Option<&ResettableKvBackendRef> {
198        self.in_memory.as_ref()
199    }
200
201    pub fn election_ref(&self) -> Option<&ElectionRef> {
202        self.election.as_ref()
203    }
204
205    pub fn meta_peer_client_ref(&self) -> Option<&MetaPeerClientRef> {
206        self.meta_peer_client.as_ref()
207    }
208
209    pub fn node_manager_ref(&self) -> Option<&NodeManagerRef> {
210        self.node_manager.as_ref()
211    }
212
213    pub async fn build(self) -> Result<Metasrv> {
214        let MetasrvBuilder {
215            election,
216            meta_peer_client,
217            options,
218            kv_backend,
219            in_memory,
220            selector,
221            handler_group_builder,
222            node_manager,
223            plugins,
224            table_metadata_allocator,
225        } = self;
226
227        let options = options.unwrap_or_default();
228        options.gc.validate()?;
229
230        let kv_backend = kv_backend.unwrap_or_else(|| Arc::new(MemoryKvBackend::new()));
231        let in_memory = in_memory.unwrap_or_else(|| Arc::new(MemoryKvBackend::new()));
232
233        let state = Arc::new(RwLock::new(match election {
234            None => State::leader(options.grpc.server_addr.clone(), true),
235            Some(_) => State::follower(options.grpc.server_addr.clone()),
236        }));
237
238        let leader_cached_kv_backend = Arc::new(LeaderCachedKvBackend::new(
239            state.clone(),
240            kv_backend.clone(),
241        ));
242
243        let meta_peer_client = meta_peer_client
244            .unwrap_or_else(|| build_default_meta_peer_client(&election, &in_memory));
245
246        let node_scheduling_manager = Arc::new(NodeSchedulingManager::new(kv_backend.clone()));
247
248        // The only place a node scheduling decorator is built. Every consumer that reads the
249        // datanode list (region placement, failover exclusions) must go through
250        // `Metasrv::selector_ctx().peer_discovery`, and building a `PeerDiscoveryRef` from the raw
251        // client is impossible by construction. Bypassing this decorator silently drops the
252        // drain fence, so do not build a second instance.
253        let selector_ctx = SelectorContext {
254            affinity_fallback: plugins
255                .as_ref()
256                .and_then(|plugins| plugins.get::<AffinityFallback>())
257                .unwrap_or_default(),
258            affinity: plugins
259                .as_ref()
260                .and_then(|plugins| plugins.get::<AffinityRef>())
261                .unwrap_or_else(|| Arc::new(NoopAffinity)),
262            peer_discovery: Arc::new(SchedulingAwarePeerDiscovery::new(
263                node_scheduling_manager.clone(),
264                meta_peer_client.clone(),
265            )),
266        };
267        // Frontends only; the decorator passes them through unchanged.
268        let database_operator =
269            Arc::new(DatabaseOperator::new(selector_ctx.peer_discovery.clone()));
270
271        let event_inserter = Box::new(InsertForwarder::new(
272            database_operator.clone(),
273            Some(InsertOptions {
274                ttl: options.event_recorder.ttl,
275                append_mode: true,
276                twcs_compaction_time_window: Some(DEFAULT_COMPACTION_TIME_WINDOW),
277            }),
278        ));
279        // Builds the event recorder to record important events and persist them as the system table.
280        let event_recorder = Arc::new(EventRecorderImpl::with_event_type_filter(
281            Box::new(EventHandlerImpl::new(event_inserter)),
282            options.event_recorder.event_types.clone(),
283            options.event_recorder.flush_interval,
284        ));
285
286        let selector = selector.unwrap_or_else(|| Arc::new(LeaseBasedSelector));
287        let pushers = Pushers::default();
288        let mailbox = build_mailbox(&kv_backend, &pushers);
289        let runtime_switch_manager = Arc::new(RuntimeSwitchManager::new(kv_backend.clone()));
290        let repartition_gc_requirement_manager =
291            Arc::new(RepartitionGcRequirementManager::new(kv_backend.clone()));
292        let procedure_manager = build_procedure_manager(
293            &options,
294            &kv_backend,
295            &runtime_switch_manager,
296            event_recorder,
297        );
298
299        let table_metadata_manager = Arc::new(TableMetadataManager::new(
300            leader_cached_kv_backend.clone() as _,
301        ));
302        let flow_metadata_manager = Arc::new(FlowMetadataManager::new(
303            leader_cached_kv_backend.clone() as _,
304        ));
305
306        let wal_provider = build_wal_provider(&options.wal, kv_backend.clone())
307            .await
308            .context(BuildWalProviderSnafu)?;
309        let wal_provider = Arc::new(wal_provider);
310        let is_remote_wal = wal_provider.is_remote_wal();
311        let table_metadata_allocator = table_metadata_allocator.unwrap_or_else(|| {
312            let sequence = Arc::new(
313                SequenceBuilder::new(TABLE_ID_SEQ, kv_backend.clone())
314                    .initial(MIN_USER_TABLE_ID as u64)
315                    .step(10)
316                    .build(),
317            );
318            let peer_allocator = Arc::new(
319                MetasrvPeerAllocator::new(selector_ctx.clone(), selector.clone())
320                    .with_max_items(MAX_REGION_SEQ),
321            );
322            Arc::new(TableMetadataAllocator::with_peer_allocator(
323                sequence,
324                wal_provider.clone(),
325                peer_allocator,
326            ))
327        });
328        let table_id_allocator = table_metadata_allocator.table_id_allocator();
329
330        let flow_selector =
331            Arc::new(RoundRobinSelector::new(SelectTarget::Flownode)) as SelectorRef;
332
333        let flow_metadata_allocator = {
334            // for now flownode just use round-robin selector
335            let flow_selector_ctx = selector_ctx.clone();
336            let peer_allocator = Arc::new(MetasrvPeerAllocator::new(
337                flow_selector_ctx,
338                flow_selector.clone(),
339            ));
340            let seq = Arc::new(
341                SequenceBuilder::new(FLOW_ID_SEQ, kv_backend.clone())
342                    .initial(MIN_USER_FLOW_ID as u64)
343                    .step(10)
344                    .build(),
345            );
346
347            Arc::new(FlowMetadataAllocator::with_peer_allocator(
348                seq,
349                peer_allocator,
350            ))
351        };
352        let flow_state_handler =
353            FlowStateHandler::new(FlowStateManager::new(in_memory.clone().as_kv_backend_ref()));
354
355        let memory_region_keeper = Arc::new(MemoryRegionKeeper::default());
356        let node_manager = node_manager.unwrap_or_else(|| {
357            let datanode_client_channel_config = options.datanode.client.channel_config();
358            Arc::new(NodeClients::new(datanode_client_channel_config))
359        });
360        let cache_invalidator = Arc::new(MetasrvCacheInvalidator::new(
361            mailbox.clone(),
362            MetasrvInfo {
363                server_addr: options.grpc.server_addr.clone(),
364            },
365        ));
366
367        if !is_remote_wal && options.enable_region_failover {
368            ensure!(
369                options.allow_region_failover_on_local_wal,
370                error::UnexpectedSnafu {
371                    violated: "Region failover is not supported in the local WAL implementation!
372                    If you want to enable region failover for local WAL, please set `allow_region_failover_on_local_wal` to true.",
373                }
374            );
375            if options.allow_region_failover_on_local_wal {
376                warn!(
377                    "Region failover is force enabled in the local WAL implementation! This may lead to data loss during failover!"
378                );
379            }
380        }
381
382        let (tx, rx) = RegionSupervisor::channel();
383        let (region_failure_detector_controller, region_supervisor_ticker): (
384            RegionFailureDetectorControllerRef,
385            Option<std::sync::Arc<RegionSupervisorTicker>>,
386        ) = if options.enable_region_failover {
387            (
388                Arc::new(RegionFailureDetectorControl::new(tx.clone())) as _,
389                Some(Arc::new(RegionSupervisorTicker::new(
390                    DEFAULT_TICK_INTERVAL,
391                    options.region_failure_detector_initialization_delay,
392                    DEFAULT_INITIALIZATION_RETRY_PERIOD,
393                    tx.clone(),
394                ))),
395            )
396        } else {
397            (Arc::new(NoopRegionFailureDetectorControl) as _, None as _)
398        };
399
400        // region migration manager
401        let region_migration_manager = Arc::new(RegionMigrationManager::new(
402            procedure_manager.clone(),
403            DefaultContextFactory::new(
404                in_memory.clone(),
405                table_metadata_manager.clone(),
406                memory_region_keeper.clone(),
407                region_failure_detector_controller.clone(),
408                mailbox.clone(),
409                options.grpc.server_addr.clone(),
410                cache_invalidator.clone(),
411            ),
412        ));
413        region_migration_manager.try_start()?;
414
415        let drain_datanode_manager = Arc::new(DrainDatanodeManager::new(
416            procedure_manager.clone(),
417            table_metadata_manager.clone(),
418            node_scheduling_manager.clone(),
419            region_migration_manager.clone(),
420            selector_ctx.clone(),
421        ));
422        drain_datanode_manager.try_start()?;
423        let affinity_resolver = plugins
424            .as_ref()
425            .and_then(|plugins| plugins.get::<SelectorFactoryRef>())
426            .and_then(|factory| {
427                factory.build_affinity_resolver(
428                    meta_peer_client.clone(),
429                    selector_ctx.peer_discovery.clone(),
430                )
431            });
432        let region_supervisor_selector = plugins
433            .as_ref()
434            .and_then(|plugins| plugins.get::<RegionStatAwareSelectorRef>());
435
436        let supervisor_selector = match region_supervisor_selector {
437            Some(selector) => {
438                info!("Using region stat aware selector");
439                RegionSupervisorSelector::RegionStatAwareSelector(selector)
440            }
441            None => RegionSupervisorSelector::NaiveSelector(selector.clone()),
442        };
443
444        let region_failover_handler = if options.enable_region_failover {
445            let region_supervisor = RegionSupervisor::new(
446                rx,
447                options.failure_detector,
448                selector_ctx.clone(),
449                supervisor_selector,
450                region_migration_manager.clone(),
451                runtime_switch_manager.clone(),
452                meta_peer_client.clone(),
453                leader_cached_kv_backend.clone(),
454            )
455            .with_state(state.clone());
456            let region_supervisor = if let Some(resolver) = &affinity_resolver {
457                region_supervisor.with_affinity_resolver(resolver.clone())
458            } else {
459                region_supervisor
460            };
461
462            Some(RegionFailureHandler::new(
463                region_supervisor,
464                HeartbeatAcceptor::new(tx),
465            ))
466        } else {
467            None
468        };
469
470        let leader_region_registry = Arc::new(LeaderRegionRegistry::default());
471        let topic_stats_registry = Arc::new(TopicStatsRegistry::default());
472
473        let ddl_context = DdlContext {
474            peer_alloc_context_provider: None,
475            affinity_fallback: selector_ctx.affinity_fallback,
476            affinity_resolver,
477            node_manager: node_manager.clone(),
478            cache_invalidator: cache_invalidator.clone(),
479            memory_region_keeper: memory_region_keeper.clone(),
480            leader_region_registry: leader_region_registry.clone(),
481            table_metadata_manager: table_metadata_manager.clone(),
482            table_metadata_allocator: table_metadata_allocator.clone(),
483            flow_metadata_manager: flow_metadata_manager.clone(),
484            flow_metadata_allocator: flow_metadata_allocator.clone(),
485            region_failure_detector_controller,
486            soft_drop_enabled: ddl_soft_drop_enabled(&options),
487            soft_drop_retention: ddl_soft_drop_retention(&options),
488            create_database_metadata_committer: None,
489        };
490        let procedure_manager_c = procedure_manager.clone();
491        let repartition_procedure_factory: RepartitionProcedureFactoryRef = if options.gc.enable {
492            Arc::new(DefaultRepartitionProcedureFactory::new(
493                mailbox.clone(),
494                options.grpc.server_addr.clone(),
495                repartition_gc_requirement_manager.clone(),
496            ))
497        } else {
498            Arc::new(GcDisabledRepartitionProcedureFactory::new(
499                mailbox.clone(),
500                options.grpc.server_addr.clone(),
501                repartition_gc_requirement_manager.clone(),
502            ))
503        };
504        let ddl_manager = DdlManager::new(
505            ddl_context,
506            procedure_manager_c,
507            repartition_procedure_factory,
508        );
509
510        let ddl_manager = if let Some(configurator) = plugins
511            .as_ref()
512            .and_then(|p| p.get::<DdlManagerConfiguratorRef<DdlManagerConfigureContext>>())
513        {
514            let ctx = DdlManagerConfigureContext {
515                kv_backend: kv_backend.clone(),
516                peer_discovery: selector_ctx.peer_discovery.clone(),
517            };
518            configurator
519                .configure(ddl_manager, ctx)
520                .await
521                .context(OtherSnafu)?
522        } else {
523            ddl_manager
524        };
525        ddl_manager
526            .register_loaders()
527            .context(error::InitDdlManagerSnafu)?;
528
529        let ddl_manager = Arc::new(ddl_manager);
530
531        let region_flush_ticker = if is_remote_wal {
532            let remote_wal_options = options.wal.remote_wal_options().unwrap();
533            let (region_flush_trigger, region_flush_ticker) = RegionFlushTrigger::new(
534                table_metadata_manager.clone(),
535                leader_region_registry.clone(),
536                topic_stats_registry.clone(),
537                mailbox.clone(),
538                options.grpc.server_addr.clone(),
539                remote_wal_options.flush_trigger_size,
540                remote_wal_options.checkpoint_trigger_size,
541                remote_wal_options.region_flush_trigger_interval,
542                remote_wal_options.periodic_checkpoint_persist_interval,
543            );
544            region_flush_trigger.try_start()?;
545
546            Some(Arc::new(region_flush_ticker))
547        } else {
548            None
549        };
550
551        // remote WAL prune ticker and manager
552        let wal_prune_ticker = if is_remote_wal && options.wal.enable_active_wal_pruning() {
553            let (tx, rx) = WalPruneManager::channel();
554            // Safety: Must be remote WAL.
555            let remote_wal_options = options.wal.remote_wal_options().unwrap();
556            let kafka_client = build_kafka_client(&remote_wal_options.connection)
557                .await
558                .context(error::BuildKafkaClientSnafu)?;
559            let wal_prune_context = WalPruneContext {
560                client: Arc::new(kafka_client),
561                table_metadata_manager: table_metadata_manager.clone(),
562                leader_region_registry: leader_region_registry.clone(),
563            };
564            let wal_prune_manager = WalPruneManager::new(
565                remote_wal_options.auto_prune_parallelism,
566                remote_wal_options.auto_prune_logical_delete,
567                rx,
568                procedure_manager.clone(),
569                wal_prune_context,
570            );
571            // Start manager in background. Ticker will be started in the main thread to send ticks.
572            wal_prune_manager.try_start().await?;
573            let wal_prune_ticker = Arc::new(WalPruneTicker::new(
574                remote_wal_options.auto_prune_interval,
575                tx.clone(),
576            ));
577            Some(wal_prune_ticker)
578        } else {
579            None
580        };
581
582        let gc_ticker = if options.gc.enable {
583            let gc_scheduler_ctx = DefaultGcSchedulerCtx::try_new(
584                table_metadata_manager.clone(),
585                procedure_manager.clone(),
586                runtime_switch_manager.clone(),
587                #[cfg(feature = "enterprise")]
588                ddl_manager.clone(),
589                meta_peer_client.clone(),
590                mailbox.clone(),
591                options.grpc.server_addr.clone(),
592            )?;
593            let (gc_scheduler, gc_ticker) = GcScheduler::new_with_config(
594                gc_scheduler_ctx,
595                runtime_switch_manager.clone(),
596                options.gc.clone(),
597            )?;
598            gc_scheduler.try_start()?;
599
600            Some(Arc::new(gc_ticker))
601        } else {
602            None
603        };
604
605        let customized_region_lease_renewer = plugins
606            .as_ref()
607            .and_then(|plugins| plugins.get::<CustomizedRegionLeaseRenewerRef>());
608
609        let persist_region_stats_handler = if !options.stats_persistence.ttl.is_zero() {
610            let inserter = Box::new(InsertForwarder::new(
611                database_operator.clone(),
612                Some(InsertOptions {
613                    ttl: options.stats_persistence.ttl,
614                    append_mode: true,
615                    twcs_compaction_time_window: Some(
616                        REGION_STATS_TABLE_TWCS_COMPACTION_TIME_WINDOW,
617                    ),
618                }),
619            ));
620
621            Some(PersistStatsHandler::new(
622                inserter,
623                options.stats_persistence.interval,
624            ))
625        } else {
626            None
627        };
628
629        let handler_group_builder = match handler_group_builder {
630            Some(handler_group_builder) => handler_group_builder,
631            None => {
632                let region_lease_handler = RegionLeaseHandler::new(
633                    default_distributed_time_constants().region_lease.as_secs(),
634                    table_metadata_manager.clone(),
635                    memory_region_keeper.clone(),
636                    customized_region_lease_renewer,
637                );
638
639                HeartbeatHandlerGroupBuilder::new(pushers)
640                    .with_plugins(plugins.clone())
641                    .with_region_failure_handler(region_failover_handler)
642                    .with_region_lease_handler(Some(region_lease_handler))
643                    .with_flush_stats_factor(Some(options.flush_stats_factor))
644                    .with_flow_state_handler(Some(flow_state_handler))
645                    .with_persist_stats_handler(persist_region_stats_handler)
646                    .add_default_handlers()
647            }
648        };
649
650        let enable_telemetry = options.enable_telemetry;
651        let metasrv_home = Path::new(&options.data_home)
652            .join(METASRV_DATA_DIR)
653            .to_string_lossy()
654            .to_string();
655
656        let reconciliation_manager = Arc::new(ReconciliationManager::new(
657            node_manager.clone(),
658            table_metadata_manager.clone(),
659            cache_invalidator.clone(),
660            procedure_manager.clone(),
661        ));
662        reconciliation_manager
663            .try_start()
664            .context(error::InitReconciliationManagerSnafu)?;
665
666        let mut resource_stat = ResourceStatImpl::default();
667        resource_stat.start_collect_cpu_usage();
668
669        Ok(Metasrv {
670            state,
671            started: Arc::new(AtomicBool::new(false)),
672            start_time_ms: common_time::util::current_time_millis() as u64,
673            options,
674            in_memory,
675            kv_backend,
676            leader_cached_kv_backend,
677            meta_peer_client: meta_peer_client.clone(),
678            selector,
679            selector_ctx,
680            // TODO(jeremy): We do not allow configuring the flow selector.
681            flow_selector,
682            handler_group: RwLock::new(None),
683            handler_group_builder: Mutex::new(Some(handler_group_builder)),
684            election,
685            procedure_manager,
686            mailbox,
687            ddl_manager,
688            wal_provider,
689            table_metadata_manager,
690            runtime_switch_manager,
691            repartition_gc_requirement_manager,
692            greptimedb_telemetry_task: get_greptimedb_telemetry_task(
693                Some(metasrv_home),
694                meta_peer_client,
695                enable_telemetry,
696            )
697            .await,
698            plugins: plugins.unwrap_or_else(Plugins::default),
699            memory_region_keeper,
700            region_migration_manager,
701            drain_datanode_manager,
702            region_supervisor_ticker,
703            cache_invalidator,
704            leader_region_registry,
705            wal_prune_ticker,
706            region_flush_ticker,
707            table_id_allocator,
708            reconciliation_manager,
709            topic_stats_registry,
710            resource_stat: Arc::new(resource_stat),
711            gc_ticker,
712            database_operator,
713            node_scheduling_manager,
714        })
715    }
716}
717
718fn build_mailbox(kv_backend: &KvBackendRef, pushers: &Pushers) -> MailboxRef {
719    let mailbox_sequence = SequenceBuilder::new("heartbeat_mailbox", kv_backend.clone())
720        .initial(1)
721        .step(100)
722        .build();
723
724    HeartbeatMailbox::create(pushers.clone(), mailbox_sequence)
725}
726
727fn build_procedure_manager(
728    options: &MetasrvOptions,
729    kv_backend: &KvBackendRef,
730    runtime_switch_manager: &RuntimeSwitchManagerRef,
731    event_recorder: EventRecorderRef,
732) -> ProcedureManagerRef {
733    let manager_config = ManagerConfig {
734        max_retry_times: options.procedure.max_retry_times,
735        retry_delay: options.procedure.retry_delay,
736        max_running_procedures: options.procedure.max_running_procedures,
737        ..Default::default()
738    };
739    let kv_state_store = Arc::new(
740        KvStateStore::new(kv_backend.clone()).with_max_value_size(
741            options
742                .procedure
743                .max_metadata_value_size
744                .map(|v| v.as_bytes() as usize),
745        ),
746    );
747
748    Arc::new(LocalManager::new(
749        manager_config,
750        kv_state_store.clone(),
751        kv_state_store,
752        Some(runtime_switch_manager.clone()),
753        Some(event_recorder),
754    ))
755}
756
757/// Resolves if soft-drop is enabled from metasrv options.
758///
759/// Soft drop is an enterprise-only feature; it is always disabled in
760/// non-enterprise builds regardless of the configuration.
761fn ddl_soft_drop_enabled(options: &MetasrvOptions) -> bool {
762    cfg!(feature = "enterprise") && options.gc.experimental_soft_drop.enable
763}
764
765/// Returns soft-drop retention for recovering persisted procedures.
766fn ddl_soft_drop_retention(options: &MetasrvOptions) -> Option<Duration> {
767    Some(options.gc.experimental_soft_drop.retention)
768}
769
770impl Default for MetasrvBuilder {
771    fn default() -> Self {
772        Self::new()
773    }
774}
775
776/// The context for [`DdlManagerConfiguratorRef`].
777pub struct DdlManagerConfigureContext {
778    pub kv_backend: KvBackendRef,
779    pub peer_discovery: PeerDiscoveryRef,
780}
781
782#[cfg(test)]
783mod tests {
784    use super::*;
785
786    #[cfg(feature = "enterprise")]
787    #[test]
788    fn test_ddl_soft_drop_gate_preserves_retention_for_recovery() {
789        let mut options = MetasrvOptions::default();
790        options.gc.enable = true;
791        options.gc.experimental_soft_drop.enable = true;
792        options.gc.experimental_soft_drop.retention = Duration::from_secs(123);
793
794        assert!(ddl_soft_drop_enabled(&options));
795        assert_eq!(
796            Some(Duration::from_secs(123)),
797            ddl_soft_drop_retention(&options)
798        );
799    }
800
801    #[cfg(not(feature = "enterprise"))]
802    #[test]
803    fn test_ddl_soft_drop_is_always_disabled_in_non_enterprise_build() {
804        let mut options = MetasrvOptions::default();
805        options.gc.enable = true;
806        options.gc.experimental_soft_drop.enable = true;
807        options.gc.experimental_soft_drop.retention = Duration::from_secs(123);
808
809        assert!(!ddl_soft_drop_enabled(&options));
810        // Retention is preserved for recovering persisted procedures.
811        assert_eq!(
812            Some(Duration::from_secs(123)),
813            ddl_soft_drop_retention(&options)
814        );
815    }
816
817    #[test]
818    fn test_ddl_soft_drop_is_disabled_by_default() {
819        assert!(!ddl_soft_drop_enabled(&MetasrvOptions::default()));
820    }
821
822    #[test]
823    fn test_soft_drop_options_are_validated() {
824        let mut options = MetasrvOptions::default();
825        options.gc.enable = true;
826        options.gc.experimental_soft_drop.enable = true;
827        options.gc.experimental_soft_drop.retention = Duration::ZERO;
828
829        assert!(options.gc.validate().is_err());
830    }
831
832    #[tokio::test]
833    async fn test_builder_rejects_soft_drop_when_gc_is_disabled() {
834        let mut options = MetasrvOptions::default();
835        options.gc.experimental_soft_drop.enable = true;
836
837        assert!(
838            MetasrvBuilder::new()
839                .options(options)
840                .build()
841                .await
842                .is_err()
843        );
844    }
845
846    #[tokio::test]
847    async fn test_builder_skips_gc_validation_when_gc_is_disabled() {
848        let mut options = MetasrvOptions::default();
849        options.gc.enable = false;
850        options.gc.max_concurrent_tables = 0;
851
852        let metasrv = MetasrvBuilder::new()
853            .options(options)
854            .build()
855            .await
856            .unwrap();
857        let context = metasrv.ddl_manager().create_context();
858        assert_eq!(context.affinity_fallback, AffinityFallback::Error);
859        assert!(context.affinity_resolver.is_none());
860        assert!(context.peer_alloc_context_provider.is_none());
861    }
862
863    #[tokio::test]
864    async fn test_builder_injects_affinity_components() {
865        use std::sync::atomic::{AtomicUsize, Ordering};
866
867        use common_meta::affinity::{
868            Affinity, AffinityRef, AffinityReference, AffinityRequirements, AffinityResolver,
869            AffinityResolverRef,
870        };
871        use common_meta::cluster::NodeInfo;
872        use common_meta::error::Result as MetaResult;
873
874        use common_meta::ddl_manager::DdlManagerConfigurator;
875
876        use crate::discovery::node_info::NodeInfoAccessor;
877        use crate::metasrv::{SelectorFactory, SelectorFactoryContext};
878
879        struct TestAffinity;
880
881        impl Affinity for TestAffinity {
882            fn matches(&self, _: &AffinityRequirements, _: &NodeInfo) -> bool {
883                true
884            }
885        }
886
887        struct TestResolver;
888
889        #[async_trait::async_trait]
890        impl AffinityResolver for TestResolver {
891            async fn resolve(
892                &self,
893                references: &[AffinityReference],
894            ) -> MetaResult<Vec<Option<AffinityRequirements>>> {
895                Ok(vec![None; references.len()])
896            }
897        }
898
899        struct RecordingFactory {
900            calls: Arc<AtomicUsize>,
901            resolver: AffinityResolverRef,
902            discovery: Arc<std::sync::Mutex<Option<PeerDiscoveryRef>>>,
903        }
904
905        struct RecordingConfigurator {
906            discovery: Arc<std::sync::Mutex<Option<PeerDiscoveryRef>>>,
907        }
908
909        #[async_trait::async_trait]
910        impl DdlManagerConfigurator<DdlManagerConfigureContext> for RecordingConfigurator {
911            async fn configure(
912                &self,
913                ddl_manager: DdlManager,
914                ctx: DdlManagerConfigureContext,
915            ) -> std::result::Result<DdlManager, common_error::ext::BoxedError> {
916                *self.discovery.lock().unwrap() = Some(ctx.peer_discovery);
917                Ok(ddl_manager)
918            }
919        }
920
921        impl SelectorFactory for RecordingFactory {
922            fn build_affinity_resolver(
923                &self,
924                _: Arc<dyn NodeInfoAccessor>,
925                peer_discovery: PeerDiscoveryRef,
926            ) -> Option<AffinityResolverRef> {
927                *self.discovery.lock().unwrap() = Some(peer_discovery);
928                self.calls.fetch_add(1, Ordering::Relaxed);
929                Some(self.resolver.clone())
930            }
931
932            fn build(&self, ctx: SelectorFactoryContext) -> SelectorRef {
933                ctx.base_selector
934            }
935        }
936
937        let factory_discovery = Arc::new(std::sync::Mutex::new(None));
938        let configurator_discovery = Arc::new(std::sync::Mutex::new(None));
939        let calls = Arc::new(AtomicUsize::new(0));
940        let resolver: AffinityResolverRef = Arc::new(TestResolver);
941        let affinity: AffinityRef = Arc::new(TestAffinity);
942        let plugins = Plugins::new();
943        plugins.insert(affinity.clone());
944        plugins.insert(AffinityFallback::Unconstrained);
945        plugins.insert(Arc::new(RecordingFactory {
946            calls: calls.clone(),
947            resolver: resolver.clone(),
948            discovery: factory_discovery.clone(),
949        }) as SelectorFactoryRef);
950        plugins.insert(Arc::new(RecordingConfigurator {
951            discovery: configurator_discovery.clone(),
952        })
953            as DdlManagerConfiguratorRef<DdlManagerConfigureContext>);
954        let metasrv = MetasrvBuilder::new()
955            .options(MetasrvOptions {
956                enable_telemetry: false,
957                ..Default::default()
958            })
959            .plugins(plugins)
960            .build()
961            .await
962            .unwrap();
963
964        assert_eq!(
965            metasrv.selector_ctx().affinity_fallback,
966            AffinityFallback::Unconstrained
967        );
968        assert_eq!(
969            metasrv.ddl_manager().create_context().affinity_fallback,
970            AffinityFallback::Unconstrained
971        );
972        assert_eq!(calls.load(Ordering::Relaxed), 1);
973        for discovery in [factory_discovery, configurator_discovery] {
974            assert!(Arc::ptr_eq(
975                discovery.lock().unwrap().as_ref().unwrap(),
976                &metasrv.selector_ctx().peer_discovery,
977            ));
978        }
979        assert!(Arc::ptr_eq(&metasrv.selector_ctx().affinity, &affinity));
980        assert!(Arc::ptr_eq(
981            metasrv
982                .ddl_manager()
983                .create_context()
984                .affinity_resolver
985                .as_ref()
986                .unwrap(),
987            &resolver
988        ));
989    }
990
991    #[tokio::test]
992    async fn test_builder_wires_node_scheduling_discovery() {
993        use common_meta::peer::Peer;
994
995        use crate::key::NodeSchedulingRole;
996        use crate::test_util::put_datanodes;
997
998        let metasrv = MetasrvBuilder::new()
999            .options(MetasrvOptions {
1000                enable_telemetry: false,
1001                ..Default::default()
1002            })
1003            .build()
1004            .await
1005            .unwrap();
1006
1007        put_datanodes(
1008            metasrv.meta_peer_client(),
1009            vec![Peer::empty(1), Peer::empty(2)],
1010        )
1011        .await;
1012        metasrv
1013            .node_scheduling_manager()
1014            .mark_unschedulable(NodeSchedulingRole::Datanode, 2)
1015            .await
1016            .unwrap();
1017
1018        let datanodes = metasrv
1019            .selector_ctx()
1020            .peer_discovery
1021            .active_datanodes(None)
1022            .await
1023            .unwrap();
1024        assert_eq!(2, datanodes.len());
1025        for datanode in datanodes {
1026            let expected = datanode.peer.id != 2;
1027            assert_eq!(Some(expected), datanode.schedulable);
1028        }
1029    }
1030}