Skip to main content

frontend/instance/
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::sync::Arc;
16use std::sync::atomic::AtomicBool;
17
18use auth::PermissionCheckerRef;
19use cache::{PARTITION_INFO_CACHE_NAME, TABLE_FLOWNODE_SET_CACHE_NAME, TABLE_ROUTE_CACHE_NAME};
20use catalog::CatalogManagerRef;
21use catalog::kvbackend::KvBackendCatalogManager;
22use catalog::process_manager::ProcessManagerRef;
23use catalog::system_schema::semantic_graph::EntityGraphProviderRef;
24use common_base::Plugins;
25use common_batcher::request_limiter::RequestLimiter;
26use common_datasource::object_store::LocalFileAccess;
27use common_event_recorder::{EventRecorderImpl, EventRecorderRef};
28use common_meta::cache::{LayeredCacheRegistryRef, TableFlownodeSetCacheRef, TableRouteCacheRef};
29use common_meta::cache_invalidator::CacheInvalidatorRef;
30use common_meta::key::TableMetadataManager;
31use common_meta::key::flow::FlowMetadataManager;
32use common_meta::kv_backend::KvBackendRef;
33use common_meta::node_manager::NodeManagerRef;
34use common_meta::procedure_executor::ProcedureExecutorRef;
35use dashmap::DashMap;
36use operator::batcher::PendingRowsBatcher;
37use operator::delete::Deleter;
38use operator::flow::FlowServiceOperator;
39use operator::insert::Inserter;
40use operator::procedure::ProcedureServiceOperator;
41use operator::request::Requester;
42#[cfg(feature = "enterprise")]
43use operator::statement::CreateDatabaseHandlerRef;
44use operator::statement::{
45    AdminEventRecorderHandle, AdminFunctionRecordingLayer, ExecutorConfigureContext,
46    StatementExecutor, StatementExecutorConfiguratorRef, StatementExecutorRef,
47};
48use operator::table::TableMutationOperator;
49use operator::write_dispatcher::WriteDispatcher;
50use partition::cache::PartitionInfoCacheRef;
51use partition::manager::PartitionRuleManager;
52use pipeline::pipeline_operator::PipelineOperator;
53use query::QueryEngineFactory;
54use query::region_query::RegionQueryHandlerFactoryRef;
55use servers::batcher::table::TablePendingRowsBatcher;
56use servers::batcher::{BatchingProtocol, pending_rows_batch_sync_enabled};
57use snafu::{OptionExt, ResultExt};
58
59use crate::error::{self, DataFusionSnafu, ExternalSnafu, Result};
60use crate::events::EventHandlerImpl;
61use crate::frontend::FrontendOptions;
62use crate::heartbeat::frontend_peer_addr;
63use crate::instance::Instance;
64use crate::instance::entity_graph::EntityGraphProviderImpl;
65use crate::instance::otlp::TraceAuxCache;
66use crate::instance::region_query::FrontendRegionQueryHandler;
67use crate::service_config::BatcherOptions;
68
69/// The frontend [`Instance`] builder.
70pub struct FrontendBuilder {
71    options: FrontendOptions,
72    kv_backend: KvBackendRef,
73    layered_cache_registry: LayeredCacheRegistryRef,
74    local_cache_invalidator: CacheInvalidatorRef,
75    catalog_manager: CatalogManagerRef,
76    node_manager: NodeManagerRef,
77    plugins: Option<Plugins>,
78    procedure_executor: ProcedureExecutorRef,
79    process_manager: ProcessManagerRef,
80    local_file_access: LocalFileAccess,
81}
82
83impl FrontendBuilder {
84    /// Creates a frontend builder from its configuration and shared services.
85    ///
86    /// Uses the layered cache registry for local invalidation unless overridden.
87    #[allow(clippy::too_many_arguments)]
88    pub fn new(
89        options: FrontendOptions,
90        kv_backend: KvBackendRef,
91        layered_cache_registry: LayeredCacheRegistryRef,
92        catalog_manager: CatalogManagerRef,
93        node_manager: NodeManagerRef,
94        procedure_executor: ProcedureExecutorRef,
95        process_manager: ProcessManagerRef,
96    ) -> Self {
97        Self {
98            options,
99            kv_backend,
100            local_cache_invalidator: layered_cache_registry.clone(),
101            layered_cache_registry,
102            catalog_manager,
103            node_manager,
104            plugins: None,
105            procedure_executor,
106            process_manager,
107            local_file_access: LocalFileAccess::Disabled,
108        }
109    }
110
111    #[cfg(test)]
112    pub(crate) fn new_test(
113        options: &FrontendOptions,
114        meta_client: meta_client::MetaClientRef,
115    ) -> Self {
116        use cache::{build_fundamental_cache_registry, with_default_composite_cache_registry};
117        use common_meta::cache::LayeredCacheRegistryBuilder;
118        use common_meta::kv_backend::memory::MemoryKvBackend;
119
120        let kv_backend = Arc::new(MemoryKvBackend::new());
121
122        // Builds cache registry
123        let layered_cache_builder = LayeredCacheRegistryBuilder::default();
124        let fundamental_cache_registry = build_fundamental_cache_registry(kv_backend.clone());
125        let layered_cache_registry = Arc::new(
126            with_default_composite_cache_registry(
127                layered_cache_builder.add_cache_registry(fundamental_cache_registry),
128            )
129            .unwrap()
130            .build(),
131        );
132
133        Self::new(
134            options.clone(),
135            kv_backend,
136            layered_cache_registry,
137            catalog::memory::MemoryCatalogManager::with_default_setup(),
138            Arc::new(client::client_manager::NodeClients::default()),
139            meta_client,
140            Arc::new(catalog::process_manager::ProcessManager::new(
141                "".to_string(),
142                None,
143            )),
144        )
145    }
146
147    /// Overrides the default local cache invalidator.
148    pub fn with_local_cache_invalidator(self, cache_invalidator: CacheInvalidatorRef) -> Self {
149        Self {
150            local_cache_invalidator: cache_invalidator,
151            ..self
152        }
153    }
154
155    pub fn with_local_file_access(mut self, local_file_access: LocalFileAccess) -> Self {
156        self.local_file_access = local_file_access;
157        self
158    }
159
160    pub fn options(&self) -> &FrontendOptions {
161        &self.options
162    }
163
164    pub fn kv_backend(&self) -> &KvBackendRef {
165        &self.kv_backend
166    }
167
168    pub fn layered_cache_registry(&self) -> &LayeredCacheRegistryRef {
169        &self.layered_cache_registry
170    }
171
172    pub fn catalog_manager(&self) -> &CatalogManagerRef {
173        &self.catalog_manager
174    }
175
176    pub fn node_manager(&self) -> &NodeManagerRef {
177        &self.node_manager
178    }
179
180    pub fn procedure_executor(&self) -> &ProcedureExecutorRef {
181        &self.procedure_executor
182    }
183
184    pub fn process_manager(&self) -> &ProcessManagerRef {
185        &self.process_manager
186    }
187
188    pub fn with_plugin(self, plugins: Plugins) -> Self {
189        Self {
190            plugins: Some(plugins),
191            ..self
192        }
193    }
194
195    /// Validates configuration and assembles the frontend [`Instance`].
196    pub async fn try_build(self) -> Result<Instance> {
197        if let Err(reason) = self.options.write_retry.validate() {
198            return error::InvalidWriteRetryConfigSnafu { reason }.fail();
199        }
200        let kv_backend = self.kv_backend;
201        let node_manager = self.node_manager;
202        let plugins = self.plugins.unwrap_or_default();
203        let process_manager = self.process_manager;
204        let table_route_cache: TableRouteCacheRef =
205            self.layered_cache_registry
206                .get()
207                .context(error::CacheRequiredSnafu {
208                    name: TABLE_ROUTE_CACHE_NAME,
209                })?;
210        let partition_info_cache: PartitionInfoCacheRef = self
211            .layered_cache_registry
212            .get()
213            .context(error::CacheRequiredSnafu {
214                name: PARTITION_INFO_CACHE_NAME,
215            })?;
216        let partition_manager = Arc::new(PartitionRuleManager::new(
217            kv_backend.clone(),
218            table_route_cache.clone(),
219            partition_info_cache.clone(),
220        ));
221
222        let local_cache_invalidator = self.local_cache_invalidator;
223
224        let mut write_dispatcher =
225            WriteDispatcher::new(node_manager.clone(), partition_manager.clone());
226        if self.options.write_retry.max_retries > 0 {
227            write_dispatcher = write_dispatcher.with_retry(
228                self.options.write_retry.clone(),
229                self.catalog_manager.clone(),
230                local_cache_invalidator.clone(),
231            );
232        }
233        let write_dispatcher = Arc::new(write_dispatcher);
234
235        let region_query_handler =
236            if let Some(factory) = plugins.get::<RegionQueryHandlerFactoryRef>() {
237                factory.build(partition_manager.clone(), node_manager.clone())
238            } else {
239                FrontendRegionQueryHandler::arc(partition_manager.clone(), node_manager.clone())
240            };
241
242        let table_flownode_cache: TableFlownodeSetCacheRef = self
243            .layered_cache_registry
244            .get()
245            .context(error::CacheRequiredSnafu {
246                name: TABLE_FLOWNODE_SET_CACHE_NAME,
247            })?;
248
249        let create_inserter = || {
250            Inserter::new(
251                self.catalog_manager.clone(),
252                partition_manager.clone(),
253                node_manager.clone(),
254                table_flownode_cache.clone(),
255                self.options.auto_create_table,
256            )
257            .with_write_dispatcher(write_dispatcher.clone())
258        };
259        // The execution-only inserter owns no batchers, avoiding an Arc cycle.
260        let bulk_inserter = Arc::new(create_inserter());
261        let build_batcher = |options: &BatcherOptions| -> Option<Arc<dyn PendingRowsBatcher>> {
262            if !options.pending_rows_batching_enabled()
263                || (self.options.prom_store.with_metric_engine
264                    && options
265                        .protocols
266                        .iter()
267                        .all(|protocol| *protocol == BatchingProtocol::Prom))
268            {
269                return None;
270            }
271            TablePendingRowsBatcher::try_new(
272                options.pending_rows_flush_interval,
273                options.max_batch_rows,
274                options.max_concurrent_flushes,
275                options.worker_channel_capacity,
276                options.max_inflight_requests,
277                options.flow_notification_queue_capacity,
278                bulk_inserter.clone(),
279            )
280            .map(|batcher| batcher as Arc<dyn PendingRowsBatcher>)
281        };
282        let inserter = Arc::new(
283            create_inserter()
284                .with_pending_rows_batcher(build_batcher(self.options.table_batcher_options())),
285        );
286        let deleter = Arc::new(
287            Deleter::new(
288                self.catalog_manager.clone(),
289                partition_manager.clone(),
290                node_manager.clone(),
291            )
292            .with_write_dispatcher(write_dispatcher),
293        );
294        let requester = Arc::new(Requester::new(
295            self.catalog_manager.clone(),
296            partition_manager.clone(),
297            node_manager.clone(),
298        ));
299        let table_mutation_handler = Arc::new(TableMutationOperator::new(
300            inserter.clone(),
301            deleter.clone(),
302            requester,
303        ));
304
305        let table_metadata_manager = Arc::new(TableMetadataManager::new(kv_backend.clone()));
306        let procedure_service_handler = Arc::new(ProcedureServiceOperator::new(
307            self.procedure_executor.clone(),
308            self.catalog_manager.clone(),
309            table_metadata_manager.clone(),
310        ));
311
312        let flow_metadata_manager: Arc<FlowMetadataManager> =
313            Arc::new(FlowMetadataManager::new(kv_backend.clone()));
314        let flow_service = FlowServiceOperator::new(flow_metadata_manager, node_manager.clone());
315
316        let mut query_options = self.options.query.clone();
317        query_options.enable_per_region_metrics = self.options.logging.enable_per_region_metrics;
318        let query_engine = QueryEngineFactory::try_new_with_plugins(
319            self.catalog_manager.clone(),
320            Some(partition_manager.clone()),
321            Some(region_query_handler.clone()),
322            Some(table_mutation_handler),
323            Some(procedure_service_handler),
324            Some(Arc::new(flow_service)),
325            true,
326            plugins.clone(),
327            query_options,
328        )
329        .context(DataFusionSnafu)?
330        .query_engine();
331
332        // Inject the entity-graph provider now that the query engine exists, so the
333        // computed `greptime_private.semantic_entities` / `semantic_relationships`
334        // tables can derive rows. Late binding here breaks the `catalog -> query`
335        // dependency cycle.
336        if let Some(kv_catalog) = self
337            .catalog_manager
338            .as_any()
339            .downcast_ref::<KvBackendCatalogManager>()
340        {
341            let provider: EntityGraphProviderRef = Arc::new(EntityGraphProviderImpl::new(
342                query_engine.clone(),
343                Arc::downgrade(&self.catalog_manager),
344                plugins.get::<PermissionCheckerRef>(),
345            ));
346            kv_catalog.set_entity_graph_provider(provider);
347        }
348
349        let frontend_peer_addr = frontend_peer_addr(&self.options);
350        let statement_executor = StatementExecutor::new(
351            self.catalog_manager.clone(),
352            query_engine.clone(),
353            self.procedure_executor,
354            kv_backend.clone(),
355            local_cache_invalidator,
356            inserter.clone(),
357            partition_manager,
358            Some(process_manager.clone()),
359            frontend_peer_addr.clone(),
360            self.local_file_access,
361        );
362
363        let statement_executor =
364            if let Some(configurator) = plugins.get::<StatementExecutorConfiguratorRef>() {
365                let ctx = ExecutorConfigureContext {
366                    kv_backend: kv_backend.clone(),
367                };
368                configurator
369                    .configure(statement_executor, ctx)
370                    .await
371                    .context(ExternalSnafu)?
372            } else {
373                statement_executor
374            };
375
376        #[cfg(feature = "enterprise")]
377        let statement_executor = if let Some(handler) = plugins.get::<CreateDatabaseHandlerRef>() {
378            statement_executor.with_create_database_handler(handler)
379        } else {
380            statement_executor
381        };
382
383        let admin_event_recorder = AdminEventRecorderHandle::default();
384        let statement_executor = statement_executor.with_admin_function_layer(Arc::new(
385            AdminFunctionRecordingLayer::new(admin_event_recorder.clone()),
386        ));
387        let statement_executor = Arc::new(statement_executor);
388
389        let pipeline_operator = Arc::new(PipelineOperator::new(
390            inserter.clone(),
391            statement_executor.clone(),
392            self.catalog_manager.clone(),
393            query_engine.clone(),
394            &self.options.pipeline,
395        ));
396
397        plugins.insert::<StatementExecutorRef>(statement_executor.clone());
398
399        let slow_query_recorder = Arc::new(EventRecorderImpl::new(Box::new(
400            EventHandlerImpl::new(statement_executor.clone(), self.options.slow_query.ttl),
401        )));
402        let event_recorder: EventRecorderRef = Arc::new(EventRecorderImpl::with_event_type_filter(
403            Box::new(EventHandlerImpl::new(
404                statement_executor.clone(),
405                self.options.event_recorder.ttl,
406            )),
407            self.options.event_recorder.event_types.clone(),
408            self.options.event_recorder.flush_interval,
409        ));
410        admin_event_recorder.install(&event_recorder);
411
412        let batcher_options = self.options.table_batcher_options();
413        let trace_aux_limiter = if batcher_options.pending_rows_batching_enabled()
414            && !pending_rows_batch_sync_enabled()
415        {
416            RequestLimiter::try_new(batcher_options.max_inflight_requests)
417        } else {
418            None
419        };
420
421        Ok(Instance {
422            logical_batcher: Default::default(),
423            frontend_peer_addr,
424            catalog_manager: self.catalog_manager,
425            pipeline_operator,
426            statement_executor,
427            query_engine,
428            plugins,
429            inserter,
430            deleter,
431            table_metadata_manager,
432            event_recorder,
433            slow_query_recorder,
434            process_manager,
435            otlp_metrics_table_legacy_cache: Arc::new(DashMap::new()),
436            slow_query_options: self.options.slow_query.clone(),
437            influxdb_default_merge_mode: self.options.influxdb.default_merge_mode,
438            trace_ingest_chunk_size: self.options.otlp.trace_ingest_chunk_size,
439            trace_aux_cache: TraceAuxCache::new(self.options.otlp.trace_aux_cache_size.as_bytes()),
440            trace_aux_limiter,
441            otlp_resource_info: self.options.otlp.experimental_enable_resource_info,
442            suspend: Arc::new(AtomicBool::new(false)),
443        })
444    }
445}
446
447#[cfg(test)]
448mod tests {
449    use api::v1::meta::Role;
450    use common_meta::cache_invalidator::{Context, DummyCacheInvalidator};
451    use common_meta::instruction::CacheIdent;
452    use common_meta::key::table_route::{TableRouteKey, TableRouteValue};
453    use common_meta::key::{MetadataKey, MetadataValue};
454    use common_meta::rpc::router::{Region, RegionRoute};
455    use common_meta::rpc::store::PutRequest;
456    use meta_client::client::MetaClientBuilder;
457    use store_api::storage::RegionId;
458
459    use super::*;
460
461    #[tokio::test]
462    async fn test_local_cache_invalidator() {
463        for custom_invalidator in [false, true] {
464            let mut builder = FrontendBuilder::new_test(
465                &FrontendOptions::default(),
466                Arc::new(MetaClientBuilder::new(0, Role::Frontend).build()),
467            );
468            if custom_invalidator {
469                builder = builder.with_local_cache_invalidator(Arc::new(DummyCacheInvalidator));
470            }
471            let backend = builder.kv_backend.clone();
472            let routes: TableRouteCacheRef = builder.layered_cache_registry.get().unwrap();
473            let partitions: PartitionInfoCacheRef = builder.layered_cache_registry.get().unwrap();
474            let instance = builder.try_build().await.unwrap();
475            let table_id = 1024;
476            for region_number in [1, 2] {
477                let route = TableRouteValue::physical(vec![RegionRoute {
478                    region: Region {
479                        id: RegionId::new(table_id, region_number),
480                        ..Default::default()
481                    },
482                    ..Default::default()
483                }]);
484                backend
485                    .put(
486                        PutRequest::new()
487                            .with_key(TableRouteKey::new(table_id).to_bytes())
488                            .with_value(route.try_as_raw_value().unwrap()),
489                    )
490                    .await
491                    .unwrap();
492                if region_number == 1 {
493                    routes.get(table_id).await.unwrap().unwrap();
494                    partitions.get(table_id).await.unwrap().unwrap();
495                }
496            }
497            assert!(routes.contains_key(&table_id));
498            assert!(partitions.contains_key(&table_id));
499
500            instance
501                .cache_invalidator()
502                .invalidate(&Context::default(), &[CacheIdent::TableId(table_id)])
503                .await
504                .unwrap();
505
506            assert_eq!(custom_invalidator, routes.contains_key(&table_id));
507            assert_eq!(custom_invalidator, partitions.contains_key(&table_id));
508            let route = routes.get(table_id).await.unwrap().unwrap();
509            let region = route.as_physical_table_route_ref().unwrap().region_routes[0]
510                .region
511                .id;
512            assert_eq!(
513                if custom_invalidator { 1 } else { 2 },
514                region.region_number()
515            );
516        }
517    }
518}