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