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