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::{
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
71pub 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 #[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 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 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 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 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 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}