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 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
68pub 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 #[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 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 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 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 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 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}