1use 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
69pub 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 #[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 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 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 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 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 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}