1#[cfg(test)]
18mod alter_test;
19#[cfg(test)]
20mod append_mode_test;
21#[cfg(test)]
22mod basic_test;
23#[cfg(test)]
24mod batch_catchup_test;
25#[cfg(test)]
26mod batch_open_test;
27#[cfg(test)]
28mod bump_committed_sequence_test;
29#[cfg(test)]
30mod catchup_test;
31#[cfg(test)]
32mod close_test;
33#[cfg(test)]
34pub(crate) mod compaction_test;
35#[cfg(test)]
36mod create_test;
37#[cfg(test)]
38mod drop_test;
39#[cfg(test)]
40mod edit_region_test;
41#[cfg(test)]
42mod file_ref_test;
43#[cfg(test)]
44mod filter_deleted_test;
45#[cfg(test)]
46mod flush_test;
47#[cfg(test)]
48mod index_build_test;
49#[cfg(any(test, feature = "test"))]
50pub mod listener;
51#[cfg(test)]
52mod merge_mode_test;
53#[cfg(test)]
54mod object_store_wal_test;
55#[cfg(test)]
56mod open_test;
57#[cfg(test)]
58mod parallel_test;
59#[cfg(test)]
60mod projection_test;
61#[cfg(test)]
62mod prune_test;
63pub mod region_hook;
64#[cfg(test)]
65mod row_selector_test;
66#[cfg(test)]
67mod scan_corrupt;
68#[cfg(test)]
69mod scan_test;
70#[cfg(test)]
71mod set_role_state_test;
72#[cfg(test)]
73mod skip_wal_test;
74#[cfg(test)]
75mod staging_test;
76#[cfg(test)]
77mod sync_test;
78#[cfg(test)]
79mod truncate_test;
80
81#[cfg(test)]
82mod copy_region_from_test;
83#[cfg(test)]
84mod remap_manifests_test;
85
86#[cfg(test)]
87mod apply_staging_manifest_test;
88#[cfg(test)]
89mod partition_filter_test;
90mod puffin_index;
91
92use std::any::Any;
93use std::collections::{HashMap, HashSet};
94use std::sync::Arc;
95use std::time::Instant;
96
97use api::region::RegionResponse;
98use async_trait::async_trait;
99use common_base::Plugins;
100use common_error::ext::BoxedError;
101use common_meta::error::UnexpectedSnafu;
102use common_meta::key::SchemaMetadataManagerRef;
103use common_recordbatch::{QueryMemoryTracker, SendableRecordBatchStream};
104use common_stat::get_total_memory_bytes;
105use common_telemetry::{debug, info, tracing, warn};
106use common_wal::options::WalOptions;
107use datafusion::execution::memory_pool::{GreedyMemoryPool, MemoryPool, UnboundedMemoryPool};
108use futures::future::{join_all, try_join_all};
109use futures::stream::{self, Stream, StreamExt};
110use object_store::manager::ObjectStoreManagerRef;
111use region_hook::RegionHookRef;
112use snafu::{OptionExt, ResultExt, ensure};
113use store_api::ManifestVersion;
114use store_api::codec::PrimaryKeyEncoding;
115use store_api::logstore::LogStore;
116use store_api::logstore::provider::{KafkaProvider, Provider};
117use store_api::metadata::{ColumnMetadata, RegionMetadataRef};
118use store_api::metric_engine_consts::{
119 MANIFEST_INFO_EXTENSION_KEY, TABLE_COLUMN_METADATA_EXTENSION_KEY,
120};
121use store_api::region_engine::{
122 BatchResponses, MitoCopyRegionFromRequest, MitoCopyRegionFromResponse, RegionEngine,
123 RegionManifestInfo, RegionRole, RegionScannerRef, RegionStatistic, RemapManifestsRequest,
124 RemapManifestsResponse, SetRegionRoleStateResponse, SettableRegionRoleState,
125 SyncRegionFromRequest, SyncRegionFromResponse,
126};
127use store_api::region_info::RegionInfoEntry;
128use store_api::region_request::{
129 AffectedRows, RegionCatchupRequest, RegionOpenRequest, RegionRequest,
130};
131use store_api::sst_entry::{ManifestSstEntry, PuffinIndexMetaEntry, StorageSstEntry};
132use store_api::storage::{FileId, FileRefsManifest, RegionId, ScanRequest, SequenceNumber};
133use tokio::sync::{Semaphore, oneshot};
134
135use crate::access_layer::RegionFilePathFactory;
136use crate::cache::{CacheManagerRef, CacheStrategy};
137use crate::config::MitoConfig;
138use crate::engine::puffin_index::{IndexEntryContext, collect_index_entries_from_puffin};
139use crate::error::{
140 IncrementalQueryStaleSnafu, InvalidRequestSnafu, JoinSnafu, MitoManifestInfoSnafu, RecvSnafu,
141 RegionNotFoundSnafu, Result, SequenceRangeUnsupportedSnafu, SerdeJsonSnafu,
142 SerializeColumnMetadataSnafu, SnapshotFenceStaleSnafu,
143};
144#[cfg(feature = "enterprise")]
145use crate::extension::BoxedExtensionRangeProviderFactory;
146use crate::gc::GcLimiterRef;
147use crate::manifest::action::RegionEdit;
148use crate::memtable::MemtableStats;
149use crate::metrics::{
150 HANDLE_REQUEST_ELAPSED, SCAN_MEMORY_EXHAUSTED_TOTAL, SCAN_MEMORY_USAGE_BYTES,
151 SCAN_REQUESTS_REJECTED_TOTAL,
152};
153use crate::read::scan_region::{ScanRegion, Scanner, exact_sequence_range};
154use crate::read::stream::ScanBatchStream;
155use crate::region::MitoRegionRef;
156use crate::region::opener::PartitionExprFetcherRef;
157use crate::region::options::parse_wal_options;
158use crate::request::{RegionEditRequest, WorkerRequest};
159use crate::sst::file::{FileMeta, RegionFileId, RegionIndexId};
160use crate::sst::file_ref::FileReferenceManagerRef;
161use crate::sst::index::intermediate::IntermediateManager;
162use crate::sst::index::puffin_manager::PuffinManagerFactory;
163use crate::wal::entry_distributor::{
164 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE, build_wal_entry_distributor_and_receivers,
165};
166use crate::wal::raw_entry_reader::{LogStoreRawEntryReader, RawEntryReader};
167use crate::worker::WorkerGroup;
168
169pub const MITO_ENGINE_NAME: &str = "mito";
170
171pub struct MitoEngineBuilder<'a, S: LogStore> {
172 data_home: &'a str,
173 config: MitoConfig,
174 log_store: Arc<S>,
175 object_store_manager: ObjectStoreManagerRef,
176 schema_metadata_manager: SchemaMetadataManagerRef,
177 file_ref_manager: FileReferenceManagerRef,
178 partition_expr_fetcher: PartitionExprFetcherRef,
179 plugins: Plugins,
180 #[cfg(feature = "enterprise")]
181 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
182}
183
184impl<'a, S: LogStore> MitoEngineBuilder<'a, S> {
185 #[allow(clippy::too_many_arguments)]
186 pub fn new(
187 data_home: &'a str,
188 config: MitoConfig,
189 log_store: Arc<S>,
190 object_store_manager: ObjectStoreManagerRef,
191 schema_metadata_manager: SchemaMetadataManagerRef,
192 file_ref_manager: FileReferenceManagerRef,
193 partition_expr_fetcher: PartitionExprFetcherRef,
194 plugins: Plugins,
195 ) -> Self {
196 Self {
197 data_home,
198 config,
199 log_store,
200 object_store_manager,
201 schema_metadata_manager,
202 file_ref_manager,
203 plugins,
204 partition_expr_fetcher,
205 #[cfg(feature = "enterprise")]
206 extension_range_provider_factory: None,
207 }
208 }
209
210 #[cfg(feature = "enterprise")]
211 #[must_use]
212 pub fn with_extension_range_provider_factory(
213 self,
214 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
215 ) -> Self {
216 Self {
217 extension_range_provider_factory,
218 ..self
219 }
220 }
221
222 pub async fn try_build(mut self) -> Result<MitoEngine> {
223 self.config.sanitize(self.data_home)?;
224
225 let config = Arc::new(self.config);
226 let region_hook = self.plugins.get::<RegionHookRef>();
229 let workers = WorkerGroup::start(
230 self.data_home,
231 config.clone(),
232 self.log_store.clone(),
233 self.object_store_manager,
234 self.schema_metadata_manager,
235 self.file_ref_manager,
236 self.partition_expr_fetcher.clone(),
237 self.plugins,
238 )
239 .await?;
240 let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(self.log_store));
241 let total_memory = get_total_memory_bytes().max(0) as u64;
242 let scan_memory_limit = config.scan_memory_limit.resolve(total_memory) as usize;
243 let scan_memory_pool = new_scan_memory_pool(scan_memory_limit);
244 let scan_memory_tracker =
245 QueryMemoryTracker::builder(scan_memory_limit, config.scan_memory_on_exhausted)
246 .on_update(|usage| {
247 SCAN_MEMORY_USAGE_BYTES.set(usage as i64);
248 })
249 .on_exhausted(|| {
250 SCAN_MEMORY_EXHAUSTED_TOTAL.inc();
251 })
252 .on_reject(|| {
253 SCAN_REQUESTS_REJECTED_TOTAL.inc();
254 })
255 .build();
256
257 let inner = EngineInner {
258 workers,
259 config,
260 wal_raw_entry_reader,
261 scan_memory_tracker,
262 scan_memory_pool,
263 region_hook,
264 #[cfg(feature = "enterprise")]
265 extension_range_provider_factory: None,
266 };
267
268 #[cfg(feature = "enterprise")]
269 let inner =
270 inner.with_extension_range_provider_factory(self.extension_range_provider_factory);
271
272 Ok(MitoEngine {
273 inner: Arc::new(inner),
274 })
275 }
276}
277
278#[derive(Clone)]
280pub struct MitoEngine {
281 inner: Arc<EngineInner>,
282}
283
284impl MitoEngine {
285 #[allow(clippy::too_many_arguments)]
287 pub async fn new<S: LogStore>(
288 data_home: &str,
289 config: MitoConfig,
290 log_store: Arc<S>,
291 object_store_manager: ObjectStoreManagerRef,
292 schema_metadata_manager: SchemaMetadataManagerRef,
293 file_ref_manager: FileReferenceManagerRef,
294 partition_expr_fetcher: PartitionExprFetcherRef,
295 plugins: Plugins,
296 ) -> Result<MitoEngine> {
297 let builder = MitoEngineBuilder::new(
298 data_home,
299 config,
300 log_store,
301 object_store_manager,
302 schema_metadata_manager,
303 file_ref_manager,
304 partition_expr_fetcher,
305 plugins,
306 );
307 builder.try_build().await
308 }
309
310 pub fn mito_config(&self) -> &MitoConfig {
311 &self.inner.config
312 }
313
314 pub fn cache_manager(&self) -> CacheManagerRef {
315 self.inner.workers.cache_manager()
316 }
317
318 pub fn file_ref_manager(&self) -> FileReferenceManagerRef {
319 self.inner.workers.file_ref_manager()
320 }
321
322 pub fn gc_limiter(&self) -> GcLimiterRef {
323 self.inner.workers.gc_limiter()
324 }
325
326 pub fn object_store_manager(&self) -> &ObjectStoreManagerRef {
327 self.inner.workers.object_store_manager()
328 }
329
330 pub fn puffin_manager_factory(&self) -> &PuffinManagerFactory {
331 self.inner.workers.puffin_manager_factory()
332 }
333
334 pub fn intermediate_manager(&self) -> &IntermediateManager {
335 self.inner.workers.intermediate_manager()
336 }
337
338 pub fn schema_metadata_manager(&self) -> &SchemaMetadataManagerRef {
339 self.inner.workers.schema_metadata_manager()
340 }
341
342 pub fn region_hook(&self) -> Option<RegionHookRef> {
345 self.inner.region_hook.clone()
346 }
347
348 pub async fn get_snapshot_of_file_refs(
350 &self,
351 file_handle_regions: impl IntoIterator<Item = RegionId>,
352 related_regions: HashMap<RegionId, HashSet<RegionId>>,
353 ) -> Result<FileRefsManifest> {
354 let file_ref_mgr = self.file_ref_manager();
355
356 let file_handle_regions = file_handle_regions.into_iter().collect::<Vec<_>>();
357 let query_regions: Vec<MitoRegionRef> = file_handle_regions
360 .into_iter()
361 .filter_map(|region_id| self.find_region(region_id))
362 .collect();
363
364 let dst_region_to_src_regions: Vec<(MitoRegionRef, HashSet<RegionId>)> = {
365 let dst2src = related_regions
366 .into_iter()
367 .flat_map(|(src, dsts)| dsts.into_iter().map(move |dst| (dst, src)))
368 .fold(
369 HashMap::<RegionId, HashSet<RegionId>>::new(),
370 |mut acc, (k, v)| {
371 let entry = acc.entry(k).or_default();
372 entry.insert(v);
373 acc
374 },
375 );
376 let mut dst_region_to_src_regions = Vec::with_capacity(dst2src.len());
377 for (dst_region, srcs) in dst2src {
378 let Some(region) = self.find_region(dst_region) else {
379 return RegionNotFoundSnafu {
380 region_id: dst_region,
381 }
382 .fail();
383 };
384 dst_region_to_src_regions.push((region, srcs));
385 }
386 dst_region_to_src_regions
387 };
388
389 file_ref_mgr
390 .get_snapshot_of_file_refs(query_regions, dst_region_to_src_regions)
391 .await
392 }
393
394 pub fn is_region_exists(&self, region_id: RegionId) -> bool {
396 self.inner.workers.is_region_exists(region_id)
397 }
398
399 pub fn is_region_opening(&self, region_id: RegionId) -> bool {
401 self.inner.workers.is_region_opening(region_id)
402 }
403
404 pub fn is_region_catching_up(&self, region_id: RegionId) -> bool {
406 self.inner.workers.is_region_catching_up(region_id)
407 }
408
409 pub fn get_region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
411 self.find_region(region_id)
412 .map(|region| region.region_statistic())
413 }
414
415 pub fn get_primary_key_encoding(&self, region_id: RegionId) -> Option<PrimaryKeyEncoding> {
417 self.find_region(region_id)
418 .map(|r| r.primary_key_encoding())
419 }
420
421 #[tracing::instrument(skip_all)]
426 pub async fn scan_to_stream(
427 &self,
428 region_id: RegionId,
429 request: ScanRequest,
430 ) -> Result<SendableRecordBatchStream, BoxedError> {
431 self.scanner(region_id, request)
432 .await
433 .map_err(BoxedError::new)?
434 .scan()
435 .await
436 }
437
438 pub async fn scan_batch(
440 &self,
441 region_id: RegionId,
442 request: ScanRequest,
443 filter_deleted: bool,
444 ) -> Result<ScanBatchStream> {
445 let mut scan_region = self.scan_region(region_id, request)?;
446 scan_region.set_filter_deleted(filter_deleted);
447 scan_region.scanner().await?.scan_batch()
448 }
449
450 pub(crate) async fn scanner(
452 &self,
453 region_id: RegionId,
454 request: ScanRequest,
455 ) -> Result<Scanner> {
456 self.scan_region(region_id, request)?.scanner().await
457 }
458
459 #[tracing::instrument(skip_all, fields(region_id = %region_id))]
461 fn scan_region(&self, region_id: RegionId, request: ScanRequest) -> Result<ScanRegion> {
462 self.inner.scan_region(region_id, request)
463 }
464
465 pub async fn edit_region(&self, region_id: RegionId, edit: RegionEdit) -> Result<()> {
470 let _timer = HANDLE_REQUEST_ELAPSED
471 .with_label_values(&["edit_region"])
472 .start_timer();
473
474 ensure!(
475 is_valid_region_edit(&edit),
476 InvalidRequestSnafu {
477 region_id,
478 reason: "invalid region edit"
479 }
480 );
481
482 let (tx, rx) = oneshot::channel();
483 let request = WorkerRequest::EditRegion(RegionEditRequest::new(region_id, edit, true, tx));
484 self.inner
485 .workers
486 .submit_to_worker(region_id, request)
487 .await?;
488 rx.await.context(RecvSnafu)?
489 }
490
491 pub async fn copy_region_from(
495 &self,
496 region_id: RegionId,
497 request: MitoCopyRegionFromRequest,
498 ) -> Result<MitoCopyRegionFromResponse> {
499 self.inner.copy_region_from(region_id, request).await
500 }
501
502 #[cfg(test)]
503 pub(crate) fn get_region(&self, id: RegionId) -> Option<crate::region::MitoRegionRef> {
504 self.find_region(id)
505 }
506
507 pub fn find_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
508 self.inner.workers.get_region(region_id)
509 }
510
511 pub fn regions(&self) -> Vec<MitoRegionRef> {
513 self.inner.workers.all_regions().collect()
514 }
515
516 fn encode_manifest_info_to_extensions(
517 region_id: &RegionId,
518 manifest_info: RegionManifestInfo,
519 extensions: &mut HashMap<String, Vec<u8>>,
520 ) -> Result<()> {
521 let region_manifest_info = vec![(*region_id, manifest_info)];
522
523 extensions.insert(
524 MANIFEST_INFO_EXTENSION_KEY.to_string(),
525 RegionManifestInfo::encode_list(®ion_manifest_info).context(SerdeJsonSnafu)?,
526 );
527 info!(
528 "Added manifest info: {:?} to extensions, region_id: {:?}",
529 region_manifest_info, region_id
530 );
531 Ok(())
532 }
533
534 fn encode_column_metadatas_to_extensions(
535 region_id: &RegionId,
536 column_metadatas: Vec<ColumnMetadata>,
537 extensions: &mut HashMap<String, Vec<u8>>,
538 ) -> Result<()> {
539 extensions.insert(
540 TABLE_COLUMN_METADATA_EXTENSION_KEY.to_string(),
541 ColumnMetadata::encode_list(&column_metadatas).context(SerializeColumnMetadataSnafu)?,
542 );
543 info!(
544 "Added column metadatas: {:?} to extensions, region_id: {:?}",
545 column_metadatas, region_id
546 );
547 Ok(())
548 }
549
550 pub fn find_memtable_and_sst_stats(
553 &self,
554 region_id: RegionId,
555 ) -> Result<(Vec<MemtableStats>, Vec<FileMeta>)> {
556 let region = self
557 .find_region(region_id)
558 .context(RegionNotFoundSnafu { region_id })?;
559
560 let version = region.version();
561 let memtable_stats = version
562 .memtables
563 .list_memtables()
564 .iter()
565 .map(|x| x.stats())
566 .collect::<Vec<_>>();
567
568 let sst_stats = version
569 .ssts
570 .levels()
571 .iter()
572 .flat_map(|level| level.files().map(|x| x.meta_ref()))
573 .cloned()
574 .collect::<Vec<_>>();
575 Ok((memtable_stats, sst_stats))
576 }
577
578 pub async fn all_ssts_from_manifest(&self) -> Vec<ManifestSstEntry> {
580 let node_id = self.inner.workers.file_ref_manager().node_id();
581 let regions = self.inner.workers.all_regions();
582
583 let mut results = Vec::new();
584 for region in regions {
585 let mut entries = region.manifest_sst_entries().await;
586 for e in &mut entries {
587 e.node_id = node_id;
588 }
589 results.extend(entries);
590 }
591
592 results
593 }
594
595 pub async fn all_index_metas(&self) -> Vec<PuffinIndexMetaEntry> {
597 let node_id = self.inner.workers.file_ref_manager().node_id();
598 let cache_manager = self.inner.workers.cache_manager();
599 let puffin_metadata_cache = cache_manager.puffin_metadata_cache().cloned();
600 let bloom_filter_cache = cache_manager.bloom_filter_index_cache().cloned();
601 let inverted_index_cache = cache_manager.inverted_index_cache().cloned();
602
603 let mut results = Vec::new();
604
605 for region in self.inner.workers.all_regions() {
606 let manifest_entries = region.manifest_sst_entries().await;
607 let access_layer = region.access_layer.clone();
608 let table_dir = access_layer.table_dir().to_string();
609 let path_type = access_layer.path_type();
610 let object_store = access_layer.object_store().clone();
611 let puffin_factory = access_layer.puffin_manager_factory().clone();
612 let path_factory = RegionFilePathFactory::new(table_dir, path_type);
613
614 let entry_futures = manifest_entries.into_iter().map(|entry| {
615 let object_store = object_store.clone();
616 let path_factory = path_factory.clone();
617 let puffin_factory = puffin_factory.clone();
618 let puffin_metadata_cache = puffin_metadata_cache.clone();
619 let bloom_filter_cache = bloom_filter_cache.clone();
620 let inverted_index_cache = inverted_index_cache.clone();
621
622 async move {
623 let Some(index_file_path) = entry.index_file_path.as_ref() else {
624 return Vec::new();
625 };
626
627 let index_version = entry.index_version;
628 let file_id = match FileId::parse_str(&entry.file_id) {
629 Ok(file_id) => file_id,
630 Err(err) => {
631 warn!(
632 err;
633 "Failed to parse puffin index file id, table_dir: {}, file_id: {}",
634 entry.table_dir,
635 entry.file_id
636 );
637 return Vec::new();
638 }
639 };
640 let region_index_id = RegionIndexId::new(
643 RegionFileId::new(entry.origin_region_id, file_id),
644 index_version,
645 );
646 let context = IndexEntryContext {
647 table_dir: &entry.table_dir,
648 index_file_path: index_file_path.as_str(),
649 region_id: entry.region_id,
650 table_id: entry.table_id,
651 region_number: entry.region_number,
652 region_group: entry.region_group,
653 region_sequence: entry.region_sequence,
654 file_id: &entry.file_id,
655 index_file_size: entry.index_file_size,
656 node_id,
657 };
658
659 let manager = puffin_factory
660 .build(object_store, path_factory)
661 .with_puffin_metadata_cache(puffin_metadata_cache);
662
663 collect_index_entries_from_puffin(
664 manager,
665 region_index_id,
666 context,
667 bloom_filter_cache,
668 inverted_index_cache,
669 )
670 .await
671 }
672 });
673
674 let mut meta_stream = stream::iter(entry_futures).buffer_unordered(8); while let Some(mut metas) = meta_stream.next().await {
676 results.append(&mut metas);
677 }
678 }
679
680 results
681 }
682
683 pub async fn all_region_infos(&self) -> Vec<RegionInfoEntry> {
685 let node_id = self.inner.workers.file_ref_manager().node_id();
686 self.inner
687 .workers
688 .all_regions()
689 .map(|region| region.region_info_entry(node_id))
690 .collect()
691 }
692
693 pub fn all_ssts_from_storage(&self) -> impl Stream<Item = Result<StorageSstEntry>> {
695 let node_id = self.inner.workers.file_ref_manager().node_id();
696 let regions = self.inner.workers.all_regions();
697
698 let mut layers_distinct_table_dirs = HashMap::new();
699 for region in regions {
700 let table_dir = region.access_layer.table_dir();
701 if !layers_distinct_table_dirs.contains_key(table_dir) {
702 layers_distinct_table_dirs
703 .insert(table_dir.to_string(), region.access_layer.clone());
704 }
705 }
706
707 stream::iter(layers_distinct_table_dirs)
708 .map(|(_, access_layer)| access_layer.storage_sst_entries())
709 .flatten()
710 .map(move |entry| {
711 entry.map(move |mut entry| {
712 entry.node_id = node_id;
713 entry
714 })
715 })
716 }
717}
718
719fn is_valid_region_edit(edit: &RegionEdit) -> bool {
723 (!edit.files_to_add.is_empty() || !edit.files_to_remove.is_empty())
724 && matches!(
725 edit,
726 RegionEdit {
727 files_to_add: _,
728 files_to_remove: _,
729 timestamp_ms: _,
730 compaction_time_window: None,
731 flushed_entry_id: None,
732 flushed_sequence: None,
733 ..
734 }
735 )
736}
737
738struct EngineInner {
740 workers: WorkerGroup,
742 config: Arc<MitoConfig>,
744 wal_raw_entry_reader: Arc<dyn RawEntryReader>,
746 scan_memory_tracker: QueryMemoryTracker,
748 scan_memory_pool: Arc<dyn MemoryPool>,
750 region_hook: Option<RegionHookRef>,
753 #[cfg(feature = "enterprise")]
754 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
755}
756
757type TopicGroupedRegionOpenRequests = HashMap<String, Vec<(RegionId, RegionOpenRequest)>>;
758
759fn prepare_batch_open_requests(
761 requests: Vec<(RegionId, RegionOpenRequest)>,
762) -> Result<(
763 TopicGroupedRegionOpenRequests,
764 Vec<(RegionId, RegionOpenRequest)>,
765)> {
766 let mut topic_to_regions: HashMap<String, Vec<(RegionId, RegionOpenRequest)>> = HashMap::new();
767 let mut remaining_regions: Vec<(RegionId, RegionOpenRequest)> = Vec::new();
768 for (region_id, request) in requests {
769 match parse_wal_options(&request.options).context(SerdeJsonSnafu)? {
770 WalOptions::Kafka(options) => {
771 topic_to_regions
772 .entry(options.topic)
773 .or_default()
774 .push((region_id, request));
775 }
776 WalOptions::RaftEngine | WalOptions::Noop | WalOptions::ObjectStore(_) => {
777 remaining_regions.push((region_id, request));
778 }
779 }
780 }
781
782 Ok((topic_to_regions, remaining_regions))
783}
784
785impl EngineInner {
786 #[cfg(feature = "enterprise")]
787 #[must_use]
788 fn with_extension_range_provider_factory(
789 self,
790 extension_range_provider_factory: Option<BoxedExtensionRangeProviderFactory>,
791 ) -> Self {
792 Self {
793 extension_range_provider_factory,
794 ..self
795 }
796 }
797
798 async fn stop(&self) -> Result<()> {
800 self.workers.stop().await
801 }
802
803 fn find_region(&self, region_id: RegionId) -> Result<MitoRegionRef> {
804 self.workers
805 .get_region(region_id)
806 .context(RegionNotFoundSnafu { region_id })
807 }
808
809 fn get_metadata(&self, region_id: RegionId) -> Result<RegionMetadataRef> {
813 let region = self.find_region(region_id)?;
815 Ok(region.metadata())
816 }
817
818 async fn open_topic_regions(
819 &self,
820 topic: String,
821 region_requests: Vec<(RegionId, RegionOpenRequest)>,
822 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
823 let now = Instant::now();
824 let region_ids = region_requests
825 .iter()
826 .map(|(region_id, _)| *region_id)
827 .collect::<Vec<_>>();
828 let provider = Provider::kafka_provider(topic);
829 let (distributor, entry_receivers) = build_wal_entry_distributor_and_receivers(
830 provider.clone(),
831 self.wal_raw_entry_reader.clone(),
832 ®ion_ids,
833 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
834 );
835
836 let mut responses = Vec::with_capacity(region_requests.len());
837 for ((region_id, request), entry_receiver) in
838 region_requests.into_iter().zip(entry_receivers)
839 {
840 let (request, receiver) =
841 WorkerRequest::new_open_region_request(region_id, request, Some(entry_receiver));
842 self.workers.submit_to_worker(region_id, request).await?;
843 responses.push(async move { receiver.await.context(RecvSnafu)? });
844 }
845
846 let distribution =
848 common_runtime::spawn_global(async move { distributor.distribute().await });
849 let responses = join_all(responses).await;
851 distribution.await.context(JoinSnafu)??;
852
853 let num_failure = responses.iter().filter(|r| r.is_err()).count();
854 info!(
855 "Opened {} regions for topic '{}', failures: {}, elapsed: {:?}",
856 region_ids.len() - num_failure,
857 provider.as_kafka_provider().unwrap(),
859 num_failure,
860 now.elapsed(),
861 );
862 Ok(region_ids.into_iter().zip(responses).collect())
863 }
864
865 async fn handle_batch_open_requests(
866 &self,
867 parallelism: usize,
868 requests: Vec<(RegionId, RegionOpenRequest)>,
869 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
870 let semaphore = Arc::new(Semaphore::new(parallelism));
871 let (topic_to_region_requests, remaining_region_requests) =
872 prepare_batch_open_requests(requests)?;
873 let mut responses =
874 Vec::with_capacity(topic_to_region_requests.len() + remaining_region_requests.len());
875
876 if !topic_to_region_requests.is_empty() {
877 let mut tasks = Vec::with_capacity(topic_to_region_requests.len());
878 for (topic, region_requests) in topic_to_region_requests {
879 let semaphore_moved = semaphore.clone();
880 tasks.push(async move {
881 let _permit = semaphore_moved.acquire().await.unwrap();
883 self.open_topic_regions(topic, region_requests).await
884 })
885 }
886 let r = try_join_all(tasks).await?;
887 responses.extend(r.into_iter().flatten());
888 }
889
890 if !remaining_region_requests.is_empty() {
891 let mut tasks = Vec::with_capacity(remaining_region_requests.len());
892 let mut region_ids = Vec::with_capacity(remaining_region_requests.len());
893 for (region_id, request) in remaining_region_requests {
894 let semaphore_moved = semaphore.clone();
895 region_ids.push(region_id);
896 tasks.push(async move {
897 let _permit = semaphore_moved.acquire().await.unwrap();
899 let (request, receiver) =
900 WorkerRequest::new_open_region_request(region_id, request, None);
901
902 self.workers.submit_to_worker(region_id, request).await?;
903
904 receiver.await.context(RecvSnafu)?
905 })
906 }
907
908 let results = join_all(tasks).await;
909 responses.extend(region_ids.into_iter().zip(results));
910 }
911
912 Ok(responses)
913 }
914
915 async fn catchup_topic_regions(
916 &self,
917 provider: Provider,
918 region_requests: Vec<(RegionId, RegionCatchupRequest)>,
919 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
920 let now = Instant::now();
921 let region_ids = region_requests
922 .iter()
923 .map(|(region_id, _)| *region_id)
924 .collect::<Vec<_>>();
925 let (distributor, entry_receivers) = build_wal_entry_distributor_and_receivers(
926 provider.clone(),
927 self.wal_raw_entry_reader.clone(),
928 ®ion_ids,
929 DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
930 );
931
932 let mut responses = Vec::with_capacity(region_requests.len());
933 for ((region_id, request), entry_receiver) in
934 region_requests.into_iter().zip(entry_receivers)
935 {
936 let (request, receiver) =
937 WorkerRequest::new_catchup_region_request(region_id, request, Some(entry_receiver));
938 self.workers.submit_to_worker(region_id, request).await?;
939 responses.push(async move { receiver.await.context(RecvSnafu)? });
940 }
941
942 let distribution =
944 common_runtime::spawn_global(async move { distributor.distribute().await });
945 let responses = join_all(responses).await;
947 distribution.await.context(JoinSnafu)??;
948
949 let num_failure = responses.iter().filter(|r| r.is_err()).count();
950 info!(
951 "Caught up {} regions for topic '{}', failures: {}, elapsed: {:?}",
952 region_ids.len() - num_failure,
953 provider.as_kafka_provider().unwrap(),
955 num_failure,
956 now.elapsed(),
957 );
958
959 Ok(region_ids.into_iter().zip(responses).collect())
960 }
961
962 async fn handle_batch_catchup_requests(
963 &self,
964 parallelism: usize,
965 requests: Vec<(RegionId, RegionCatchupRequest)>,
966 ) -> Result<Vec<(RegionId, Result<AffectedRows>)>> {
967 let mut responses = Vec::with_capacity(requests.len());
968 let mut topic_regions: HashMap<Arc<KafkaProvider>, Vec<_>> = HashMap::new();
969 let mut remaining_region_requests = vec![];
970
971 for (region_id, request) in requests {
972 match self.workers.get_region(region_id) {
973 Some(region) => match region.provider.as_kafka_provider() {
974 Some(provider) => {
975 topic_regions
976 .entry(provider.clone())
977 .or_default()
978 .push((region_id, request));
979 }
980 None => {
981 remaining_region_requests.push((region_id, request));
982 }
983 },
984 None => responses.push((region_id, RegionNotFoundSnafu { region_id }.fail())),
985 }
986 }
987
988 let semaphore = Arc::new(Semaphore::new(parallelism));
989
990 if !topic_regions.is_empty() {
991 let mut tasks = Vec::with_capacity(topic_regions.len());
992 for (provider, region_requests) in topic_regions {
993 let semaphore_moved = semaphore.clone();
994 tasks.push(async move {
995 let _permit = semaphore_moved.acquire().await.unwrap();
997 self.catchup_topic_regions(Provider::Kafka(provider), region_requests)
998 .await
999 })
1000 }
1001
1002 let r = try_join_all(tasks).await?;
1003 responses.extend(r.into_iter().flatten());
1004 }
1005
1006 if !remaining_region_requests.is_empty() {
1007 let mut tasks = Vec::with_capacity(remaining_region_requests.len());
1008 let mut region_ids = Vec::with_capacity(remaining_region_requests.len());
1009 for (region_id, request) in remaining_region_requests {
1010 let semaphore_moved = semaphore.clone();
1011 region_ids.push(region_id);
1012 tasks.push(async move {
1013 let _permit = semaphore_moved.acquire().await.unwrap();
1015 let (request, receiver) =
1016 WorkerRequest::new_catchup_region_request(region_id, request, None);
1017
1018 self.workers.submit_to_worker(region_id, request).await?;
1019
1020 receiver.await.context(RecvSnafu)?
1021 })
1022 }
1023
1024 let results = join_all(tasks).await;
1025 responses.extend(region_ids.into_iter().zip(results));
1026 }
1027
1028 Ok(responses)
1029 }
1030
1031 async fn handle_request(
1033 &self,
1034 region_id: RegionId,
1035 request: RegionRequest,
1036 ) -> Result<AffectedRows> {
1037 let region_metadata = self.get_metadata(region_id).ok();
1038 let (request, receiver) =
1039 WorkerRequest::try_from_region_request(region_id, request, region_metadata)?;
1040 self.workers.submit_to_worker(region_id, request).await?;
1041
1042 receiver.await.context(RecvSnafu)?
1043 }
1044
1045 fn get_committed_sequence(&self, region_id: RegionId) -> Result<SequenceNumber> {
1047 self.find_region(region_id)
1049 .map(|r| r.find_committed_sequence())
1050 }
1051
1052 #[tracing::instrument(skip_all, fields(region_id = %region_id))]
1054 fn scan_region(&self, region_id: RegionId, mut request: ScanRequest) -> Result<ScanRegion> {
1055 let query_start = Instant::now();
1056 let region = self.find_region(region_id)?;
1058 let series_index = region.series_index_store.as_ref().map(|store| {
1062 crate::series_index::SeriesIndexReadContext {
1063 store: store.clone(),
1064 version: region.series_index_version(),
1065 }
1066 });
1067 let version_data = region.version_control.current();
1068 let version = version_data.version;
1069
1070 if request.snapshot_on_scan && request.memtable_max_sequence.is_none() {
1071 request.memtable_max_sequence = Some(version_data.committed_sequence);
1072 }
1073
1074 let exact_selection = if request.exact_sequence_range {
1078 match exact_sequence_range(&request, &version) {
1079 Ok((files, sequence_range)) => Some((files, sequence_range)),
1080 Err(err) => {
1081 debug!(
1082 "Scan region {} exact sequence range denied: min={:?}, max={:?}, denial_reason=foreign_file_missing_barrier",
1083 region_id, request.memtable_min_sequence, request.memtable_max_sequence,
1084 );
1085 return Err(err);
1086 }
1087 }
1088 } else {
1089 None
1090 };
1091 let exact_sequence_range = exact_selection
1092 .as_ref()
1093 .and_then(|(_, sequence_range)| *sequence_range);
1094
1095 #[cfg(feature = "enterprise")]
1102 let extension_provider_blocks_exact = region.is_follower()
1103 && self.extension_range_provider_factory.is_some()
1104 && exact_sequence_range.is_some();
1105 #[cfg(not(feature = "enterprise"))]
1106 let extension_provider_blocks_exact = false;
1107 let exact_sequence_range = if extension_provider_blocks_exact {
1108 None
1109 } else {
1110 exact_sequence_range
1111 };
1112 let exact_selection = exact_selection.map(|(files, _)| (files, exact_sequence_range));
1113 let exact_denial_reason = if !request.exact_sequence_range {
1114 "not_requested"
1115 } else if request.skip_sst_files {
1116 "sst_files_skipped"
1117 } else if request.memtable_min_sequence.is_none() {
1118 "missing_lower_bound"
1119 } else if request.memtable_max_sequence.is_none() {
1120 "missing_upper_bound"
1121 } else if !version.options.preserve_row_sequence {
1122 "preserve_row_sequence_disabled"
1123 } else if extension_provider_blocks_exact {
1124 "extension_provider"
1125 } else if exact_sequence_range.is_none() {
1126 "selected_file_barrier_not_admitted"
1127 } else {
1128 "none"
1129 };
1130
1131 debug!(
1132 "Scan region {} exact sequence range: requested={}, min={:?}, max={:?}, committed={}, flushed={}, available={}, selected_files={}, denial_reason={}",
1133 region_id,
1134 request.exact_sequence_range,
1135 request.memtable_min_sequence,
1136 request.memtable_max_sequence,
1137 version_data.committed_sequence,
1138 version.flushed_sequence,
1139 exact_sequence_range.is_some(),
1140 exact_selection.as_ref().map_or(0, |(files, _)| files.len()),
1141 exact_denial_reason,
1142 );
1143 validate_sequence_fences(
1144 &request,
1145 &version,
1146 region_id,
1147 exact_sequence_range.is_some(),
1148 extension_provider_blocks_exact,
1149 )?;
1150
1151 let cache_manager = self.workers.cache_manager();
1153
1154 let scan_region = ScanRegion::new(
1155 version,
1156 region.access_layer.clone(),
1157 request,
1158 CacheStrategy::EnableAll(cache_manager),
1159 )
1160 .with_series_index(series_index)
1161 .with_ignore_range_index(!self.config.experimental_enable_range_index)
1162 .with_query_stat_counters(region.region_stats.query_stat_counters())
1163 .with_max_concurrent_scan_files(self.config.max_concurrent_scan_files)
1164 .with_scan_memory_pool(self.scan_memory_pool.clone())
1165 .with_experimental_series_scan_v2(self.config.experimental_series_scan_v2)
1166 .with_ignore_inverted_index(self.config.inverted_index.apply_on_query.disabled())
1167 .with_ignore_fulltext_index(self.config.fulltext_index.apply_on_query.disabled())
1168 .with_ignore_bloom_filter(self.config.bloom_filter_index.apply_on_query.disabled())
1169 .with_start_time(query_start);
1170 let scan_region = if let Some(selection) = exact_selection {
1171 scan_region.with_exact_selection(selection)
1172 } else {
1173 scan_region
1174 };
1175
1176 #[cfg(feature = "enterprise")]
1177 let scan_region = self.maybe_fill_extension_range_provider(scan_region, region);
1178
1179 Ok(scan_region)
1180 }
1181
1182 #[cfg(feature = "enterprise")]
1183 fn maybe_fill_extension_range_provider(
1184 &self,
1185 mut scan_region: ScanRegion,
1186 region: MitoRegionRef,
1187 ) -> ScanRegion {
1188 if region.is_follower()
1189 && let Some(factory) = self.extension_range_provider_factory.as_ref()
1190 {
1191 scan_region
1192 .set_extension_range_provider(factory.create_extension_range_provider(region));
1193 }
1194 scan_region
1195 }
1196
1197 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<()> {
1199 let region = self.find_region(region_id)?;
1200 region.set_role(role);
1201 Ok(())
1202 }
1203
1204 async fn set_region_role_state_gracefully(
1206 &self,
1207 region_id: RegionId,
1208 region_role_state: SettableRegionRoleState,
1209 ) -> Result<SetRegionRoleStateResponse> {
1210 let (request, receiver) =
1213 WorkerRequest::new_set_readonly_gracefully(region_id, region_role_state);
1214 self.workers.submit_to_worker(region_id, request).await?;
1215
1216 receiver.await.context(RecvSnafu)
1217 }
1218
1219 async fn sync_region(
1220 &self,
1221 region_id: RegionId,
1222 manifest_info: RegionManifestInfo,
1223 ) -> Result<(ManifestVersion, bool)> {
1224 ensure!(manifest_info.is_mito(), MitoManifestInfoSnafu);
1225 let manifest_version = manifest_info.data_manifest_version();
1226 let (request, receiver) =
1227 WorkerRequest::new_sync_region_request(region_id, manifest_version);
1228 self.workers.submit_to_worker(region_id, request).await?;
1229
1230 receiver.await.context(RecvSnafu)?
1231 }
1232
1233 async fn remap_manifests(
1234 &self,
1235 request: RemapManifestsRequest,
1236 ) -> Result<RemapManifestsResponse> {
1237 let region_id = request.region_id;
1238 let (request, receiver) = WorkerRequest::try_from_remap_manifests_request(request)?;
1239 self.workers.submit_to_worker(region_id, request).await?;
1240 let manifest_paths = receiver.await.context(RecvSnafu)??;
1241 Ok(RemapManifestsResponse { manifest_paths })
1242 }
1243
1244 async fn copy_region_from(
1245 &self,
1246 region_id: RegionId,
1247 request: MitoCopyRegionFromRequest,
1248 ) -> Result<MitoCopyRegionFromResponse> {
1249 let (request, receiver) =
1250 WorkerRequest::try_from_copy_region_from_request(region_id, request)?;
1251 self.workers.submit_to_worker(region_id, request).await?;
1252 let response = receiver.await.context(RecvSnafu)??;
1253 Ok(response)
1254 }
1255
1256 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
1257 self.workers
1258 .get_region(region_id)
1259 .map(|region| region.region_role())
1260 }
1261}
1262
1263fn validate_sequence_fences(
1264 request: &ScanRequest,
1265 version: &crate::region::version::Version,
1266 region_id: RegionId,
1267 exact_sequence_range_available: bool,
1268 extension_provider_blocks_exact: bool,
1269) -> Result<()> {
1270 if request.exact_sequence_range {
1271 if !exact_sequence_range_available {
1277 if let Some(given_seq) = request.memtable_min_sequence {
1278 let min_readable_seq = version.flushed_sequence;
1279 ensure!(
1280 given_seq >= min_readable_seq,
1281 IncrementalQueryStaleSnafu {
1282 region_id,
1283 given_seq,
1284 min_readable_seq,
1285 }
1286 );
1287 }
1288
1289 if let Some(given_seq) = request.memtable_max_sequence {
1290 let min_enforceable_seq = version.flushed_sequence;
1291 ensure!(
1292 given_seq >= min_enforceable_seq,
1293 SnapshotFenceStaleSnafu {
1294 region_id,
1295 given_seq,
1296 min_enforceable_seq,
1297 }
1298 );
1299 }
1300
1301 return SequenceRangeUnsupportedSnafu {
1307 region_id,
1308 min_seq: request.memtable_min_sequence.unwrap_or_default(),
1309 max_seq: request.memtable_max_sequence.unwrap_or_default(),
1310 reason: sequence_range_unsupported_reason(version, extension_provider_blocks_exact),
1311 }
1312 .fail();
1313 }
1314 } else {
1315 if let Some(given_seq) = request.memtable_min_sequence {
1319 let min_readable_seq = version.flushed_sequence;
1320 ensure!(
1321 given_seq >= min_readable_seq,
1322 IncrementalQueryStaleSnafu {
1323 region_id,
1324 given_seq,
1325 min_readable_seq,
1326 }
1327 );
1328 }
1329
1330 if let Some(given_seq) = request.memtable_max_sequence
1331 && !request.skip_sst_files
1332 {
1333 let min_enforceable_seq = version.flushed_sequence;
1340 ensure!(
1341 given_seq >= min_enforceable_seq,
1342 SnapshotFenceStaleSnafu {
1343 region_id,
1344 given_seq,
1345 min_enforceable_seq,
1346 }
1347 );
1348 }
1349 }
1350
1351 Ok(())
1352}
1353
1354fn sequence_range_unsupported_reason(
1355 version: &crate::region::version::Version,
1356 extension_provider_blocks_exact: bool,
1357) -> String {
1358 if extension_provider_blocks_exact {
1359 "region is a follower with an extension range provider, whose streams cannot be filtered by sequence".to_string()
1360 } else if version.options.preserve_row_sequence {
1361 "region has files without preserved per-row sequences".to_string()
1362 } else {
1363 "region does not preserve per-row sequences (preserve_row_sequence is off)".to_string()
1364 }
1365}
1366
1367fn map_batch_responses(responses: Vec<(RegionId, Result<AffectedRows>)>) -> BatchResponses {
1368 responses
1369 .into_iter()
1370 .map(|(region_id, response)| {
1371 (
1372 region_id,
1373 response.map(RegionResponse::new).map_err(BoxedError::new),
1374 )
1375 })
1376 .collect()
1377}
1378
1379#[async_trait]
1380impl RegionEngine for MitoEngine {
1381 fn name(&self) -> &str {
1382 MITO_ENGINE_NAME
1383 }
1384
1385 #[tracing::instrument(skip_all)]
1386 async fn handle_batch_open_requests(
1387 &self,
1388 parallelism: usize,
1389 requests: Vec<(RegionId, RegionOpenRequest)>,
1390 ) -> Result<BatchResponses, BoxedError> {
1391 self.inner
1393 .handle_batch_open_requests(parallelism, requests)
1394 .await
1395 .map(map_batch_responses)
1396 .map_err(BoxedError::new)
1397 }
1398
1399 #[tracing::instrument(skip_all)]
1400 async fn handle_batch_catchup_requests(
1401 &self,
1402 parallelism: usize,
1403 requests: Vec<(RegionId, RegionCatchupRequest)>,
1404 ) -> Result<BatchResponses, BoxedError> {
1405 self.inner
1406 .handle_batch_catchup_requests(parallelism, requests)
1407 .await
1408 .map(map_batch_responses)
1409 .map_err(BoxedError::new)
1410 }
1411
1412 #[tracing::instrument(skip_all)]
1413 async fn handle_request(
1414 &self,
1415 region_id: RegionId,
1416 request: RegionRequest,
1417 ) -> Result<RegionResponse, BoxedError> {
1418 let _timer = HANDLE_REQUEST_ELAPSED
1419 .with_label_values(&[request.request_type()])
1420 .start_timer();
1421
1422 let is_alter = matches!(request, RegionRequest::Alter(_));
1423 let is_create = matches!(request, RegionRequest::Create(_));
1424 let mut response = self
1425 .inner
1426 .handle_request(region_id, request)
1427 .await
1428 .map(RegionResponse::new)
1429 .map_err(BoxedError::new)?;
1430
1431 if is_alter {
1432 self.handle_alter_response(region_id, &mut response)
1433 .map_err(BoxedError::new)?;
1434 } else if is_create {
1435 self.handle_create_response(region_id, &mut response)
1436 .map_err(BoxedError::new)?;
1437 }
1438
1439 Ok(response)
1440 }
1441
1442 #[tracing::instrument(skip_all)]
1443 async fn handle_query(
1444 &self,
1445 region_id: RegionId,
1446 request: ScanRequest,
1447 ) -> Result<RegionScannerRef, BoxedError> {
1448 self.scan_region(region_id, request)
1449 .map_err(BoxedError::new)?
1450 .region_scanner()
1451 .await
1452 .map_err(BoxedError::new)
1453 }
1454
1455 fn query_memory_tracker(&self) -> Option<QueryMemoryTracker> {
1456 Some(self.inner.scan_memory_tracker.clone())
1457 }
1458
1459 async fn get_committed_sequence(
1460 &self,
1461 region_id: RegionId,
1462 ) -> Result<SequenceNumber, BoxedError> {
1463 self.inner
1464 .get_committed_sequence(region_id)
1465 .map_err(BoxedError::new)
1466 }
1467
1468 async fn get_metadata(
1470 &self,
1471 region_id: RegionId,
1472 ) -> std::result::Result<RegionMetadataRef, BoxedError> {
1473 self.inner.get_metadata(region_id).map_err(BoxedError::new)
1474 }
1475
1476 async fn stop(&self) -> std::result::Result<(), BoxedError> {
1482 self.inner.stop().await.map_err(BoxedError::new)
1483 }
1484
1485 fn region_statistic(&self, region_id: RegionId) -> Option<RegionStatistic> {
1486 self.get_region_statistic(region_id)
1487 }
1488
1489 fn set_region_role(&self, region_id: RegionId, role: RegionRole) -> Result<(), BoxedError> {
1490 self.inner
1491 .set_region_role(region_id, role)
1492 .map_err(BoxedError::new)
1493 }
1494
1495 async fn set_region_role_state_gracefully(
1496 &self,
1497 region_id: RegionId,
1498 region_role_state: SettableRegionRoleState,
1499 ) -> Result<SetRegionRoleStateResponse, BoxedError> {
1500 let _timer = HANDLE_REQUEST_ELAPSED
1501 .with_label_values(&["set_region_role_state_gracefully"])
1502 .start_timer();
1503
1504 self.inner
1505 .set_region_role_state_gracefully(region_id, region_role_state)
1506 .await
1507 .map_err(BoxedError::new)
1508 }
1509
1510 async fn sync_region(
1511 &self,
1512 region_id: RegionId,
1513 request: SyncRegionFromRequest,
1514 ) -> Result<SyncRegionFromResponse, BoxedError> {
1515 let manifest_info = request
1516 .into_region_manifest_info()
1517 .context(UnexpectedSnafu {
1518 err_msg: "Expected a manifest info request",
1519 })
1520 .map_err(BoxedError::new)?;
1521 let (_, synced) = self
1522 .inner
1523 .sync_region(region_id, manifest_info)
1524 .await
1525 .map_err(BoxedError::new)?;
1526
1527 Ok(SyncRegionFromResponse::Mito { synced })
1528 }
1529
1530 async fn remap_manifests(
1531 &self,
1532 request: RemapManifestsRequest,
1533 ) -> Result<RemapManifestsResponse, BoxedError> {
1534 self.inner
1535 .remap_manifests(request)
1536 .await
1537 .map_err(BoxedError::new)
1538 }
1539
1540 fn role(&self, region_id: RegionId) -> Option<RegionRole> {
1541 self.inner.role(region_id)
1542 }
1543
1544 fn as_any(&self) -> &dyn Any {
1545 self
1546 }
1547}
1548
1549impl MitoEngine {
1550 fn handle_alter_response(
1551 &self,
1552 region_id: RegionId,
1553 response: &mut RegionResponse,
1554 ) -> Result<()> {
1555 if let Some(statistic) = self.region_statistic(region_id) {
1556 Self::encode_manifest_info_to_extensions(
1557 ®ion_id,
1558 statistic.manifest,
1559 &mut response.extensions,
1560 )?;
1561 }
1562 let column_metadatas = self
1563 .inner
1564 .find_region(region_id)
1565 .ok()
1566 .map(|r| r.metadata().column_metadatas.clone());
1567 if let Some(column_metadatas) = column_metadatas {
1568 Self::encode_column_metadatas_to_extensions(
1569 ®ion_id,
1570 column_metadatas,
1571 &mut response.extensions,
1572 )?;
1573 }
1574 Ok(())
1575 }
1576
1577 fn handle_create_response(
1578 &self,
1579 region_id: RegionId,
1580 response: &mut RegionResponse,
1581 ) -> Result<()> {
1582 let column_metadatas = self
1583 .inner
1584 .find_region(region_id)
1585 .ok()
1586 .map(|r| r.metadata().column_metadatas.clone());
1587 if let Some(column_metadatas) = column_metadatas {
1588 Self::encode_column_metadatas_to_extensions(
1589 ®ion_id,
1590 column_metadatas,
1591 &mut response.extensions,
1592 )?;
1593 }
1594 Ok(())
1595 }
1596}
1597
1598#[cfg(any(test, feature = "test"))]
1600#[allow(clippy::too_many_arguments)]
1601impl MitoEngine {
1602 pub async fn new_for_test<S: LogStore>(
1604 data_home: &str,
1605 mut config: MitoConfig,
1606 log_store: Arc<S>,
1607 object_store_manager: ObjectStoreManagerRef,
1608 write_buffer_manager: Option<crate::flush::WriteBufferManagerRef>,
1609 listener: Option<crate::engine::listener::EventListenerRef>,
1610 time_provider: crate::time_provider::TimeProviderRef,
1611 schema_metadata_manager: SchemaMetadataManagerRef,
1612 file_ref_manager: FileReferenceManagerRef,
1613 partition_expr_fetcher: PartitionExprFetcherRef,
1614 ) -> Result<MitoEngine> {
1615 config.sanitize(data_home)?;
1616
1617 let config = Arc::new(config);
1618 let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(log_store.clone()));
1619 let total_memory = get_total_memory_bytes().max(0) as u64;
1620 let scan_memory_limit = config.scan_memory_limit.resolve(total_memory) as usize;
1621 let scan_memory_pool = new_scan_memory_pool(scan_memory_limit);
1622 let scan_memory_tracker =
1623 QueryMemoryTracker::builder(scan_memory_limit, config.scan_memory_on_exhausted)
1624 .on_update(|usage| {
1625 SCAN_MEMORY_USAGE_BYTES.set(usage as i64);
1626 })
1627 .on_exhausted(|| {
1628 SCAN_MEMORY_EXHAUSTED_TOTAL.inc();
1629 })
1630 .on_reject(|| {
1631 SCAN_REQUESTS_REJECTED_TOTAL.inc();
1632 })
1633 .build();
1634 Ok(MitoEngine {
1635 inner: Arc::new(EngineInner {
1636 workers: WorkerGroup::start_for_test(
1637 data_home,
1638 config.clone(),
1639 log_store,
1640 object_store_manager,
1641 write_buffer_manager,
1642 listener,
1643 schema_metadata_manager,
1644 file_ref_manager,
1645 time_provider,
1646 partition_expr_fetcher,
1647 )
1648 .await?,
1649 config,
1650 wal_raw_entry_reader,
1651 scan_memory_tracker,
1652 scan_memory_pool,
1653 region_hook: None,
1654 #[cfg(feature = "enterprise")]
1655 extension_range_provider_factory: None,
1656 }),
1657 })
1658 }
1659
1660 pub fn purge_scheduler(&self) -> &crate::schedule::scheduler::SchedulerRef {
1662 self.inner.workers.purge_scheduler()
1663 }
1664}
1665
1666fn new_scan_memory_pool(scan_memory_limit: usize) -> Arc<dyn MemoryPool> {
1667 if scan_memory_limit == 0 {
1668 Arc::new(UnboundedMemoryPool::default())
1669 } else {
1670 Arc::new(GreedyMemoryPool::new(scan_memory_limit))
1671 }
1672}
1673
1674#[cfg(test)]
1675mod tests {
1676 use std::time::Duration;
1677
1678 use datafusion::execution::memory_pool::MemoryConsumer;
1679
1680 use super::*;
1681 use crate::sst::file::FileMeta;
1682
1683 #[test]
1684 fn test_is_valid_region_edit() {
1685 let edit = RegionEdit {
1687 files_to_add: vec![FileMeta::default()],
1688 files_to_remove: vec![],
1689 timestamp_ms: None,
1690 compaction_time_window: None,
1691 flushed_entry_id: None,
1692 flushed_sequence: None,
1693 committed_sequence: None,
1694 };
1695 assert!(is_valid_region_edit(&edit));
1696
1697 let edit = RegionEdit {
1699 files_to_add: vec![],
1700 files_to_remove: vec![],
1701 timestamp_ms: None,
1702 compaction_time_window: None,
1703 flushed_entry_id: None,
1704 flushed_sequence: None,
1705 committed_sequence: None,
1706 };
1707 assert!(!is_valid_region_edit(&edit));
1708
1709 let edit = RegionEdit {
1711 files_to_add: vec![],
1712 files_to_remove: vec![FileMeta::default()],
1713 timestamp_ms: None,
1714 compaction_time_window: None,
1715 flushed_entry_id: None,
1716 flushed_sequence: None,
1717 committed_sequence: None,
1718 };
1719 assert!(is_valid_region_edit(&edit));
1720
1721 let edit = RegionEdit {
1723 files_to_add: vec![FileMeta::default()],
1724 files_to_remove: vec![FileMeta::default()],
1725 timestamp_ms: None,
1726 compaction_time_window: None,
1727 flushed_entry_id: None,
1728 flushed_sequence: None,
1729 committed_sequence: None,
1730 };
1731 assert!(is_valid_region_edit(&edit));
1732
1733 let edit = RegionEdit {
1735 files_to_add: vec![FileMeta::default()],
1736 files_to_remove: vec![],
1737 timestamp_ms: None,
1738 compaction_time_window: Some(Duration::from_secs(1)),
1739 flushed_entry_id: None,
1740 flushed_sequence: None,
1741 committed_sequence: None,
1742 };
1743 assert!(!is_valid_region_edit(&edit));
1744 let edit = RegionEdit {
1745 files_to_add: vec![FileMeta::default()],
1746 files_to_remove: vec![],
1747 timestamp_ms: None,
1748 compaction_time_window: None,
1749 flushed_entry_id: Some(1),
1750 flushed_sequence: None,
1751 committed_sequence: None,
1752 };
1753 assert!(!is_valid_region_edit(&edit));
1754 let edit = RegionEdit {
1755 files_to_add: vec![FileMeta::default()],
1756 files_to_remove: vec![],
1757 timestamp_ms: None,
1758 compaction_time_window: None,
1759 flushed_entry_id: None,
1760 flushed_sequence: Some(1),
1761 committed_sequence: None,
1762 };
1763 assert!(!is_valid_region_edit(&edit));
1764 }
1765
1766 #[test]
1767 fn test_scan_memory_pool_is_shared_across_consumers() {
1768 let pool = new_scan_memory_pool(100);
1769 let cloned_pool = pool.clone();
1770 let first = MemoryConsumer::new("first-scan").register(&pool);
1771 let second = MemoryConsumer::new("second-scan").register(&cloned_pool);
1772
1773 first.try_grow(60).unwrap();
1774 assert!(second.try_grow(50).is_err());
1775 second.try_grow(40).unwrap();
1776 assert_eq!(100, pool.reserved());
1777 }
1778}