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